Вращающееся меню навигации?

126
02 февраля 2021, 01:40

Товарищи, подскажите! Куда копать, возможно ли это вообще - как реализовать такой круг в html, чтобы при клике на конкретное меню - оно вращалось и выбранное меню становилось на верх (как колесо фортуны, но не рандом).

Интересует так же, как это изобразить в коде? Достаточно только css или нужно что-то еще? Есть какие то примеры, наводки?

Update:

Заново прошу помощи! Есть вот такой круг (спасибо MaximLensky): codepen.io/topicstarter/pen/dExqLr

Как сделать из него семиугольник (здесь пятиугольник). А так же добавить сверху такие меню, как на картинке?

Как его анимировать, чтобы при нажатии на элемент - круг вращался и наверх по центру становился выбранный элемент, изменялся цвет? Дополнительные меню сверху - просто ссылки, такого же цвета, как и основной элемент.

Важно, чтобы текст и цвета можно было изменить.

Так же прикрепляю примерно что-то похожее (вращение): codepen.io/AndersFly666/pen/GZxVYO

Answer 1

Что касается верстки всяких разных кругов, тут ваши лучшие друзья это Math.sin Math.cos и Math.PI

Вот пример с использованием d3.js, но могло быть и без него

PS: длинный текст надо тоже вписать в круг и его вращать

Самая актуальная версия тут https://codepen.io/strangerintheq/pen/wbVRGL

let data = Array(5).fill(0).map(object) 
let a = Math.PI*2/data.length, start = -Math.PI/2 
 
let r1 = 30, r2 = 35, r3 = 37, r4 = 95; 
let basePts = []; 
basePts.push(pt(0, r1)) 
basePts.push(pt(a, r2)) 
basePts.push(pt(a*2-0.15, r3)) 
basePts.push(pt(a*1.5-0.1, r4)) 
let arc = `A ${r4} ${r4} 0 0 1 ${pt(a/2, r4)}` ; 
 
append('path', 'line')  
  .attr('stroke', d => d.color) 
  .attr('fill', 'none') 
  .attr('d', 'M' + basePts.join('L') + arc) 
  .attr('transform', (d, i) => `rotate(${-i*360/data.length})`) 
 
append('text', 'icon') 
  .attr('fill', d => d.color) 
  .html(d => d.icon) 
  .attr('x', (e, i) => pt(a*i+a*0.7,80)[0]) 
  .attr('y', (e, i) => pt(a*i+a*0.7,80)[1]) 
 
append('text', 'number') 
  .attr('fill', d => d.color) 
  .html((d, i) => i+1) 
  .attr('x', (e, i) => pt(a*i+a*1.5,44)[0]) 
  .attr('y', (e, i) => pt(a*i+a*1.5,44)[1]) 
 
basePts.shift(); 
 
append('path', 'active')  
  .attr('d', 'M' + basePts.join('L') + arc) 
  .attr('transform', (d, i) => `rotate(${-i*360/data.length})`) 
  .on('click', (d,i) => { 
    let angle = (i + 1)*360 / data.length 
    d3.select('g').attr('transform', `rotate(${angle})`) 
    d3.selectAll('text').attr('transform', function(){ 
        let x = d3.select(this).attr('x') 
        let y = d3.select(this).attr('y') 
        return `rotate(${-angle},${x},${y})`; 
    }) 
  })   
 
function append(node, clazz) { 
    return d3.select('g') 
          .selectAll(node + '.' + clazz) 
          .data(data) 
          .enter() 
          .append(node) 
          .classed(clazz, true) 
} 
 
function pt (a, r) { 
    a = - a - Math.PI/2 
    return [Math.cos(a)*r, Math.sin(a)*r]; 
} 
 
function object(){ 
    return { 
        color: `hsl(${Math.random()*360},55%,55%)`, 
        icon: `&#xf0${Math.floor(Math.random()*100)};` 
    } 
}
text { 
    stroke: transparent; 
    dominant-baseline: middle; 
    text-anchor: middle; 
} 
 
.icon { 
    font-family: FontAwesome; 
    font-size: 20px; 
    transition: 1s; 
} 
 
.number { 
    font-family: arial, sans-serif; 
    font-size: 15px; 
    transition: 1s; 
} 
 
path.active{ 
    fill: transparent; 
    cursor: pointer; 
} 
 
g { 
    transition: 1s; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> 
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"> 
<svg viewbox="-100 -100 200 200" width="500" height="500"> 
    <g></g> 
</svg>

UPD:

Классика жанра, в макете 5 углов, но надо 7, как хорошо что я не умею делать это в inkscape, я просто поменяю пару цифр в своем коде

let data = Array(7).fill(0).map(object) 
let a = Math.PI*2/data.length, start = -Math.PI/2 
 
let r1=30, r2=35, r3=37, r4=95, rotation=0; 
let pts = []; 
pts.push(pt(0, r1)) 
pts.push(pt(a, r2)) 
pts.push(pt(a*2-0.15, r3)) 
pts.push(pt(a*1.5-0.45, r4)) 
let arc = (radius, angle) => `A ${radius} ${radius} 0 0 1 ${pt(angle, radius)}` ; 
 
append('path', 'line')  
  .attr('stroke', d => d.color) 
  .attr('d', 'M' + pts.join('L') + arc(r4, a-0.7)) 
  .attr('transform', (d, i) => `rotate(${-i*360/data.length})`) 
 
append('text', 'icon rotate') 
  .attr('fill', d => d.color) 
  .html(d => d.icon) 
  .attr('x', (e, i) => pt(a*i+a*0.35,80)[0]) 
  .attr('y', (e, i) => pt(a*i+a*0.35,80)[1]) 
 
append('path', 'text-wrap-path') 
  .attr('id', (d, i) => `text-wrap-path_${i}`) 
  .attr('x', (e, i) => pt(a*i+a*0.77,67)[0]) 
  .attr('y', (e, i) => pt(a*i+a*0.77,67)[1]) 
  .attr('d', (e, i) => { 
     let c = pt(a*i+a*0.77,67); 
     return Array(5).fill(0).map((e,i) => line(Math.acos(1-2/5*i))) 
     
     function line(a) { 
         let p1 = pt(a, 21), p2 = pt(-a, 21); 
         return 'M' + (c[0]+p1[0]) + ',' + (c[1]+p1[1]) +  
                'L' + (c[0]+p2[0]) + ',' + (c[1]+p2[1]) 
     } 
  }) 
 
  append('text', 'text')  
      .append('textPath') 
      .attr('href', (e,i) => `#text-wrap-path_${i}`) 
      .text(d => d.text) 
      .attr('fill', d => d.color) 
      .attr('startOffset', 40) 
 
append('text', 'number rotate') 
  .attr('fill', d => d.color) 
  .html((d, i) => i + 1) 
  .attr('x', (e, i) => pt(a*i+a*1.2,44)[0]) 
  .attr('y', (e, i) => pt(a*i+a*1.2,44)[1]) 
 
pts.shift(); 
 
append('path', 'active')  
  .attr('fill', 'transparent') 
  .attr('d', 'M' + pts.join('L') + arc(r4, a-0.7)) 
  .attr('transform', (d, i) => `rotate(${-i*360/data.length})`) 
  .on('click', click)   
 
lineAndText(r4+15, 0) 
lineAndText(r4+30, 1) 
lineAndText(r4+45, 2) 
 
function lineAndText(r,n){ 
     
    append('path', 'line1 l' + n)  
      .attr('id', (d, i) => `line1_l${n}_${i}`) 
      .attr('stroke', d => d.color) 
      .attr('d', 'M' + pt(a-0.05*(n+1), r) + arc(r, 0.2-0.05*(n+1))) 
      .attr('transform', (d, i) => `rotate(${-i*360/data.length})`) 
 
    append('text', 'menu l' + n)  
      .append('textPath') 
      .attr('href', (e,i) => `#line1_l${n}_${i}`) 
      .text(d => d.menu[n]) 
      .attr('fill', d => d.color) 
      .attr('startOffset', 40) 
} 
 
function click(d,i) { 
    let angle = (i+1)*360/data.length-30 
    let from = rotation; 
    rotation = angle; 
     
    d3.selectAll('.line1').style('opacity', 0) 
    d3.selectAll('.line1.index_'+i).style('opacity', 1) 
     
    d3.selectAll('.menu').style('opacity', 0) 
    d3.selectAll('.menu.index_'+i).style('opacity', 1) 
     
    d3.select('g').transition().duration(1000) 
      .attrTween('transform', () => t => `rotate(${from+(angle-from)*t})`) 
     
    d3.selectAll('.text-wrap-path').transition().duration(1000) 
      .attrTween('transform', function() { 
        let x = d3.select(this).attr('x') 
        let y = d3.select(this).attr('y') 
        return t => `rotate(-${from+(angle-from)*t}, ${x}, ${y})` 
    }) 
     
    d3.selectAll('text.rotate').transition().duration(1000) 
      .attrTween('transform', function() { 
        let x = d3.select(this).attr('x') 
        let y = d3.select(this).attr('y') 
        return t => `rotate(-${from+(angle-from)*t}, ${x}, ${y})` 
    }) 
} 
 
function append(node, clazz) { 
    return d3.select('g') 
          .selectAll(node + '.' + clazz) 
          .data(data) 
          .enter() 
          .append(node) 
          .attr('fill', 'none') 
          .attr('class', (d, i) => `${clazz} index_${i}`) 
} 
 
function pt (a, r) { 
    a = - a - Math.PI/2 
    return [Math.cos(a)*r, Math.sin(a)*r]; 
} 
 
function object(){ 
    return { 
        color: `hsl(${Math.random()*360},55%,55%)`, 
        icon: `&#xf0${Math.floor(Math.random()*100)};`, 
        menu: [`menu 1`, `menu 2`, `menu 3`], 
        text: 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur'.split(' ').filter(() => Math.random() > 0.5).join(' ') 
    } 
}
text { 
    stroke: transparent; 
    dominant-baseline: middle; 
    text-anchor: middle; 
} 
 
.icon { 
    font-family: FontAwesome; 
    font-size: 18px; 
} 
 
.number { 
    font-family: arial, sans-serif; 
    font-size: 15px; 
} 
 
path.active{ 
    cursor: pointer; 
} 
 
.line1, .menu { 
    opacity: 0; 
    transition: 1s; 
} 
text.menu{ 
    dominant-baseline: text-before-edge; 
    font-size:13px; 
    cursor:pointer; 
} 
 
textPath { 
    transition: 500ms; 
} 
 
text.menu:hover textPath { 
    fill: red; 
} 
text.text { 
    font-size: 8px; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> 
<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"> 
<svg viewbox="-100 -150 200 300" width="500" height="700"> 
    <g></g> 
</svg>

Answer 2

Накидал макет на Svg в редакторе векторной графики Inkscape Для этого я вырезал в Gimp нужный фрагмент, и загрузил в inkscape, преимущественно пользовался инструментом path без закрутия пути

Символы добавил с font-awesome что потребовало подключения самого шрифта с cdn и обычное копирование и вставка самой иконки

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> 
 
<svg version="1.1" viewBox="0 0 568.39 329.08" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
  <marker id="marker1292" overflow="visible" orient="auto"> 
   <path transform="matrix(-.8 0 0 -.8 -10 0)" d="m0 0 5-5-17.5 5 17.5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt"/> 
  </marker> 
  <marker id="Arrow1Lend" overflow="visible" orient="auto"> 
   <path transform="matrix(-.8 0 0 -.8 -10 0)" d="m0 0 5-5-17.5 5 17.5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt"/> 
  </marker> 
  <marker id="marker1208" overflow="visible" orient="auto"> 
   <path transform="matrix(-1.1,0,0,-1.1,-1.1,0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> 
  </marker> 
  <marker id="marker1174" overflow="visible" orient="auto"> 
   <path transform="matrix(-1.1,0,0,-1.1,-1.1,0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> 
  </marker> 
  <marker id="Arrow2Lend" overflow="visible" orient="auto"> 
   <path transform="matrix(-1.1,0,0,-1.1,-1.1,0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> 
  </marker> 
 </defs> 
 <g transform="translate(-5 32.083)"> 
  <g transform="translate(149.58 .86069)"> 
   <rect x="-144.08" y="-32.444" width="367.39" height="328.08" fill="#fff" stroke="transparent" stroke-linecap="round" stroke-linejoin="bevel" style="paint-order:stroke markers fill"/> 
   <g fill="none" stroke="#000"> 
    <path d="m35.396 82.306-57.363 41.502 21.142 66.32-89.21-.59735c-17.233-54.667-7.8892-103.2 27.29-145.77" marker-end="url(#Arrow2Lend)"/> 
    <path d="m91.741 116.06-56.73-41.022-56.845 40.079-26.902-84.719c53.508-44.43 105.68-33.535 148.52-20.664" marker-end="url(#marker1174)"/> 
    <path d="m77.508 181.81 22.118-66.616-57.845-42.758 73.334-52.118c28.655 18.903 70.385 47.877 65.292 136.9" marker-end="url(#marker1208)"/> 
    <path d="m10.691 187.15 71.454.39738 21.784-67.126 71.171 52.474c-14.133 60.803-55.838 88.928-108.28 104.32" marker-end="url(#Arrow1Lend)"/> 
    <path d="m-13.898 127.55 20.874 66.841 69.305-.31712-27.105 83.665c-83.009.10248-106.6-36.028-132.83-70.543" marker-end="url(#marker1292)"/> 
   </g> 
   <g fill="#000000" letter-spacing="0px" word-spacing="0px"> 
    <g font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458"> 
     <text x="69.018448" y="80.646423" style="line-height:1.25" xml:space="preserve"><tspan x="69.018448" y="80.646423" font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458">1</tspan></text> 
     <text x="102.28035" y="156.99762" style="line-height:1.25" xml:space="preserve"><tspan x="102.28035" y="156.99762" font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458">2</tspan></text> 
     <text x="38.78035" y="224.27737" style="line-height:1.25" xml:space="preserve"><tspan x="38.78035" y="224.27737" font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458">3</tspan></text> 
     <text x="-35.302982" y="176.65237" style="line-height:1.25" xml:space="preserve"><tspan x="-35.302982" y="176.65237" font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458">4</tspan></text> 
     <text x="-17.916077" y="88.205948" style="line-height:1.25" xml:space="preserve"><tspan x="-17.916077" y="88.205948" font-family="sans-serif" font-size="22.578px" font-weight="bold" stroke-width=".26458">5</tspan></text> 
    </g> 
    <g font-family="FontAwesome"> 
     <text transform="scale(.93541 1.069)" x="66.752991" y="28.498955" font-size="18.708px" stroke-width=".46771" style="line-height:1.25" xml:space="preserve"><tspan x="66.752991" y="28.498955" stroke-width=".46771"></tspan></text> 
     <text transform="scale(.90135 1.1094)" x="-72.378448" y="69.372223" font-size="19.414px" stroke-width=".48534" style="line-height:1.25" xml:space="preserve"><tspan x="-72.378448" y="69.372223" stroke-width=".48534"></tspan></text> 
     <text transform="scale(1.0408 .96077)" x="-62.316162" y="224.04454" font-size="22.418px" stroke-width=".56045" style="line-height:1.25" xml:space="preserve"><tspan x="-62.316162" y="224.04454" stroke-width=".56045"></tspan></text> 
     <text transform="scale(1.0378 .96357)" x="66.140442" y="267.99738" font-size="20.754px" stroke-width=".51884" style="line-height:1.25" xml:space="preserve"><tspan x="66.140442" y="267.99738" stroke-width=".51884"> </tspan></text> 
     <text transform="scale(.92582 1.0801)" x="160.60985" y="130.09097" font-size="21.602px" stroke-width=".54006" style="line-height:1.25" xml:space="preserve"><tspan x="160.60985" y="130.09097" stroke-width=".54006"></tspan></text> 
    </g> 
    <g font-family="'Arial Black'" stroke-width=".26458"> 
     <text x="-86.254166" y="96.974998" font-size="5.6444px" style="line-height:1.25" xml:space="preserve"><tspan x="-86.254166" y="96.974998">Lorem Ipsum is</tspan><tspan x="-86.254166" y="104.03056">simply dummy </tspan><tspan x="-86.254166" y="111.08611">text of the </tspan><tspan x="-86.254166" y="118.14166">printing and </tspan><tspan x="-86.254166" y="125.19722">typesetting </tspan><tspan x="-86.254166" y="132.25278">industry. Lorem </tspan><tspan x="-86.254166" y="139.30833">Ipsum has been the</tspan><tspan x="-86.254166" y="146.36389">industry's standard</tspan></text> 
     <text x="-22.225" y="31.887501" font-size="5.6444px" style="line-height:1.25" xml:space="preserve"><tspan x="-22.225" y="31.887501">Contrary to popular </tspan><tspan x="-22.225" y="38.943058">belief, Lorem Ipsum is </tspan><tspan x="-22.225" y="45.998611">not simply random</tspan></text> 
     <g font-size="6.35px"> 
      <text x="94.720833" y="57.816666" style="line-height:1.25" xml:space="preserve"><tspan x="94.720833" y="57.816666">t is a long </tspan><tspan x="94.720833" y="65.754166">established fact </tspan><tspan x="94.720833" y="73.691666">that a reader will </tspan><tspan x="94.720833" y="81.629166">be distracted by the</tspan><tspan x="94.720833" y="89.566666"> readable content </tspan><tspan x="94.720833" y="97.530678">(<tspan font-family="Arial">of a page when</tspan>) </tspan></text> 
      <text x="93.133331" y="182.17084" style="line-height:1.25" xml:space="preserve"><tspan x="93.133331" y="182.17084">Contrary to popular</tspan><tspan x="93.133331" y="190.10834">belief, Lorem </tspan><tspan x="93.133331" y="198.04584">Ipsum is not simply</tspan><tspan x="93.133331" y="205.98334">random text.</tspan></text> 
      <text x="-31.75" y="218.15417" style="line-height:1.25" xml:space="preserve"><tspan x="-31.75" y="218.15417">Contrary to </tspan><tspan x="-31.75" y="226.09167">popular belief, </tspan><tspan x="-31.75" y="234.02917">Lorem Ipsum is not </tspan><tspan x="-31.75" y="241.96667">simply random text.</tspan></text> 
     </g> 
    </g> 
   </g> 
  </g> 
 </g> 
</svg>

READ ALSO
Командна в командной строке не работает

Командна в командной строке не работает

Набираю в командной строке net use - показывает все сетевые дискиВ моем c# коде - пусто

108
Как вывести заголовок к изображению из БД

Как вывести заголовок к изображению из БД

Всем доброго времени суток! Реализовал загрузку изображений в БД и вывод их во вью, но никак не могу решить проблемуПри выгрузке titla'a к изображениям,...

131
Входная строка имела не верный формат

Входная строка имела не верный формат

Выдает ошибку что входная строка имела не верный форматЕсли ячейка будет пустой или заполнена будет символами то постоянно выдает эту ошибку

120