Canvas кнопка меню

141
03 сентября 2021, 16:00

Всем привет! Подскажите, пожалуйста: какая технология используется для реализации кнопки меню как на сайте https://www.panoply.co.uk/? Заказчик просит скопировать.. Вижу что canvas, но не могу понять в ручную ли написан код или может фреймворк какой.. Заранее благодарен за любую информацию.

Answer 1

Вот собрал вам нечто похожее из подручных средств, в этом примере нет WebGL, все посчитано и нарисовано через 2d контекст.

let ctx = canvas.getContext('2d'); 
let k = 300, a1 = 0, a2 = 0, dir = 0, far = 300,  
    w = canvas.width, h = canvas.height, planes = []; 
 
ctx.translate(w/2, h/2); 
 
for (var i=-20; i<21; i+=20) { 
    addRect(10,10,20,i,-20) 
    addRect(10,10,-20,i,-20) 
    addDiag(15,10,20,i,-20) 
    addDiag(-15,10,20,i,-20) 
} 
addTopDiag(25,15,-25,20) 
addTopDiag(-25,-15,-25,20) 
 
function addTopDiag(x1,x2,dy,dz) { 
    whitePlane([ 
        pt(-x1, dy, -dz), 
        pt(-x2, dy, -dz), 
        pt(x1, dy, dz), 
        pt(x2, dy, dz) 
    ]);   
} 
 
function addDiag(w, h, dx=0, dy=0, dz=0){     
    whitePlane([ 
        pt(0, h/2+dy, -5), 
        pt(0, -h/2+dy, -5), 
        pt(w, -h/2+dy, dz), 
        pt(w, h/2+dy, dz) 
    ]); 
} 
 
function addRect(w, h, dx=0, dy=0, dz=0){     
    whitePlane([ 
        pt(-w/2+dx, -h/2+dy, dz), 
        pt(-w/2+dx, h/2+dy, dz), 
        pt(w/2+dx, h/2+dy, dz), 
        pt(w/2+dx, -h/2+dy,dz) 
    ]); 
} 
     
requestAnimationFrame(render) 
 
function whitePlane(points){ 
    planes.push({color: 'white', points}); 
} 
 
function pt(x,y,z) { 
    return {x, y, z}; 
} 
 
function project(p) { 
  let sinA1 = Math.sin(a1), cosA1 = Math.cos(a1); 
  let sinA2 = Math.sin(a2), cosA2 = Math.cos(a2); 
  let x = p.x*cosA1 + p.z*sinA1; 
  let z = p.z*cosA1 - p.x*sinA1; 
  let y = p.y*cosA2 +   z*sinA2; 
  let kd = k/(z*cosA2 - p.y*sinA2 + far); 
  p.X = kd*x; 
  p.Y = kd*y; 
} 
 
function render() { 
  a2 += dir * 0.1; 
  if (a2 < -Math.PI/2 || a2 > 0) { 
    dir = 0; 
    a2 = a2 > 0 ? 0 : -Math.PI/2;   
  }  
  if (a2 < 0 && a2 > -Math.PI/2) 
    requestAnimationFrame(render); 
  planes.forEach(plane => plane.points.forEach(project));	   
  ctx.clearRect(-w/2, -h/2, w, h); 
  planes.forEach(drawPlane); 
} 
 
function drawPlane(plane) { 
    ctx.fillStyle = plane.color; 
    ctx.beginPath(); 
    ctx.moveTo(plane.points[0].X, plane.points[0].Y); 
    for (var i = 1; i < plane.points.length; i++) 
        ctx.lineTo(plane.points[i].X, plane.points[i].Y); 
    ctx.fill(); 
    ctx.closePath(); 
} 
 
function toggle() { 
  if (a2 < 0 && a2 > -Math.PI/2) 
    return; 
  dir = a2 === 0 ? -1 : 1 
  requestAnimationFrame(render); 
}
body { 
  background: url(https://picsum.photos/id/22/700/200); 
} 
 
canvas { 
  cursor: pointer; 
}
<canvas id=canvas width=90 height=90 onclick="toggle()" ></canvas>

Answer 2

В отличие от соседнего ответа, посчитал почти все точки по формуле и нарисовал при помощи SVG...

let cos = Math.cos, sin = Math.sin,  
    k = 1000, a1 = 0, a2 = 0, far = 1000,  
    dir = 0, planes = []; 
 
let pts = Array(8).fill(0).map((e, i) => { 
  let a = Math.PI*(.25+.5*Math.floor(i/2)) + (i%2?.2:-.2); 
  return [cos(a)*30, sin(a)*30]; 
}); 
 
let t = -25; 
whitePlane([pt(0,t),pt(1,t),pt(4,t),pt(5,t)]) 
whitePlane([pt(2,t),pt(3,t),pt(6,t),pt(7,t)]) 
for (var i=0; i<3;i++){ 
    let t2 = t+20*i; 
    let t3 = -15+20*i; 
    whitePlane([ 
        pt(4,t2),pt(5,t2),{x:0,y:t2,z:-7},pt(6,t2),pt(7,t2), 
        pt(7,t3),pt(6,t3),{x:0,y:t3,z:-7},pt(5,t3),pt(4,t3), 
    ]) 
} 
     
requestAnimationFrame(render) 
 
function whitePlane(points) { 
 let id = "plane_" + planes.length; 
 svg.innerHTML += `<polygon id="${id}" ></polygon>`; 
 planes.push({points, id}); 
} 
 
function pt(i, y=0) { 
    return {x:pts[i][0], y, z:pts[i][1]}; 
} 
 
function project(p) { 
  let x = p.x*cos(a1) + p.z*sin(a1); 
  let z = p.z*cos(a1) - p.x*sin(a1); 
  let y = p.y*cos(a2) +   z*sin(a2); 
  let d =   z*cos(a2) - p.y*sin(a2) + far; 
  return [ (k/d)*x ,(k/d)*y ]; 
} 
 
function render() { 
  a2 += dir * 0.1 
  if (a2 < -Math.PI/2 || a2 > 0){ 
    dir = 0; 
    a2 = a2 > 0 ? 0 : -Math.PI/2;   
  }  
  dir !== 0 && requestAnimationFrame(render) 
  planes.forEach(plane => window[plane.id] 
       .setAttribute("points", plane.points.map(project)));    
} 
 
function toggle() { 
  if (dir !== 0) 
    return; 
  requestAnimationFrame(render);   
  dir = a2 === 0 ? -1 : 1 
}
body { 
    background-color: black; 
    overflow: hidden; 
} 
 
svg { 
    width: 200px; 
    height: 200px; 
    cursor: pointer; 
} 
 
polygon { 
  fill: white; 
}
<svg id=svg onclick=toggle() viewbox="-50 -50 100 100"></svg>

READ ALSO
Создать анимацию с помощью setInterval

Создать анимацию с помощью setInterval

При прочтении книги в ней попалась задачка, вроде пустяк, но я никак не могу понять, как правильно это реализовать, подскажите кто нибудь

246
Отслеживание изменений объекта

Отслеживание изменений объекта

Как отследить изменения в объекте?

138
Удаление элементов из state по таймеру

Удаление элементов из state по таймеру

Идея такая: есть локальный state (hook), в нем массив из четырех элементовНа экране есть кнопка добавления нового элемента в этот массив

96
Uncaught ReferenceError: require is not defined. Как избавиться?

Uncaught ReferenceError: require is not defined. Как избавиться?

Собираю проект через GulpТакая проблема: написал код, после "прогона" через babel выдал ошибку: regeneratorRuntime is not defined

164