前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >掘金五边形「战士」

掘金五边形「战士」

作者头像
Jimmy_is_jimmy
发布2023-09-01 12:21:18
3120
发布2023-09-01 12:21:18
举报
文章被收录于专栏:call_me_R

灵感来源

某日,进入掘金我的倔友分看到五个维度的分数图,遂想着实现以下。

五边形,对应五个维度:社区基础,社区学习,社区规范,社区影响力,社区活跃

那么,我能否是个满级的五边形战士呢?就像下面这样:

So, It's my show time now~ Hold On.

实现讲解

五边形特效实现的效果如下 GIF 图:

实现的功能参考掘金我的倔友分,使用 canvas 技术实现

html 代码的骨架很简单:

代码语言:javascript
复制
<canvas id="canvas" width="374" height="342" style="background-color: rgb(35, 35, 35)"></canvas>

宽 374,高 342。为什么选择这个数值呢?抄的掘金 canvas 数值~

接下来的就是重头戏了,编写 javascript

五边形网格绘制

看过我之前文章 - 天幕:六边形特效 的读者可能比较好理解。之前,我们绘制的是六边形:

不同的点是,这次我们绘制的是五边形,道理都一样,不明白的可以移步天幕:六边形特效文章了解,这里不赘述。

代码语言:javascript
复制
(function() {
  let canvas = document.getElementById("canvas"); // 获取画布
  let ctx = canvas.getContext("2d"); // 设置画笔
  ctx.strokeStyle = "#fff";
  ctx.lineWidth = 1;
  
  function init() {
    for(let i = 0; i < 5; i += 1) {
      // 使用半透明颜色来模拟细线条效果
      ctx.globalAlpha = 0.2;
      // 绘制五边形
      drawPentagon(canvas.width/2, canvas.height/2, i * 20);
      if(i === 4) {
        // 将五边形连接起来
        drawPentagonBetweenLine(canvas.width/2, canvas.height/2, i * 20);
      }
      // 恢复透明度
      ctx.globalAlpha = 1;
    }
  }
  init();
  
  function drawPentagon(x, y, radius) {
    ctx.strokeStyle = "#fff";
    ctx.beginPath();
    ctx.moveTo(x + Math.cos(-90 * Math.PI / 180) * radius, y + Math.sin(-90 * Math.PI / 180) * radius);
    ctx.arc(x + Math.cos(-90 * Math.PI / 180) * radius, y + Math.sin(-90 * Math.PI / 180) * radius, 0.5, 0, 2 * Math.PI);

    for(let i = 1; i <= 4; i++) {
      let angle = (-90 * Math.PI / 180) + i * (2 * Math.PI / 5);
      ctx.lineTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
      ctx.arc(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius, 0.5, 0, 2 * Math.PI);
    } 
    ctx.closePath();
    ctx.stroke();
  }
  
  function drawPentagonBetweenLine(x, y, radius) {
    ctx.beginPath();
    ctx.moveTo(x, y);
    for(let i = 0; i < 5; i += 1) {
      let angle = (-90 * Math.PI / 180) + i * (2 * Math.PI / 5);
      ctx.lineTo(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius);
      ctx.moveTo(x, y);
    } 
    ctx.closePath();
    ctx.stroke();
  }
  
})()

上面的代码,我们将使用了一个 for 循环,将四层五边形绘制出来,然后调用 drawPentagonBetweenLine 方法,将这四层五边形连接起来。

五维度点设定

OK,我们设定五个维度的维度值。

在此之前,我们得设定下五个维度的文本,我们在 drawPentagonBetweenLine 函数上添加:

代码语言:javascript
复制
// 恢复透明度
ctx.globalAlpha = 1;
ctx.beginPath();
for(let i = 0; i < 5; i += 1) {
  let angle = (-90 * Math.PI / 180) + i * (2 * Math.PI / 5);
  let _text = "";
  let _x = x + Math.cos(angle) * radius;
  let _y = y + Math.sin(angle) * radius;
  if(i === 0) {
    _text = "社区基础"
  } 
  if(i === 1) {
    _text = "社区学习"
  }
  if(i === 2) {
    _text = "社区规范"
  }
  if(i === 3) {
    _text = "社区影响力"
  }
  if(i === 4) {
    _text = "社区活跃"
  }
  const textWidth = ctx.measureText(_text).width;
  if(i === 0) {
    _y -= 10;
  } 
  if(i === 1) {
    _x += (textWidth / 2 + 10);
  }
  if(i === 2) {
    _y += 10;
  }
  if(i === 3) {
    _y += 10;
  }
  if(i === 4) {
    _x -= (textWidth / 2 + 10);
  }


  ctx.fillText(_text, _x, _y);
}
ctx.closePath();

嗯~,我们这里简单使用了 if 语句进行判断并赋值。

现在,我们可以设定五个维度对应的值了,拿社区基础为例子:

代码语言:javascript
复制
// 设定指定的维度点值
function drawPoints() {
  let x = canvas.width/2, y = canvas.height/2;
  ctx.strokeStyle = "#FDB452";
  ctx.fillStyle = "#FDB452";
  
  ctx.beginPath();
  let point0_x = x + Math.cos((-90 * Math.PI / 180) + 0 * (2 * Math.PI / 5)) * 4 * 20;
  let point0_y = y + Math.sin((-90 * Math.PI / 180) + 0 * (2 * Math.PI / 5)) * 4 * 20;
  ctx.arc(point0_x, point0_y, 2, 0, 2 * Math.PI);
  ctx.closePath();
  ctx.stroke();
  ctx.fill();
}

这里,我们计算了维度 社区基础 值点所在的位置 point0_xpoint0_y,即对应的 xy 轴,然后调用 ctx.arc() 方法进行绘制。以此类推,我们可以得到对应的其他四个维度的点坐标并绘制。

五维度区域动效

五维度的动效:先显示各个维度的点,然后从中心向四周点进行扩散。

首先,我们先将五个维度的点连线起来:

代码语言:javascript
复制
let scale = 0; // 是否正在变大, 0 -> 1
function drawLine() {
  clearCanvas();
  let point0_x = x + Math.cos((-90 * Math.PI / 180) + 0 * (2 * Math.PI / 5)) * 4 * 20 * scale;
  let point0_y = y + Math.sin((-90 * Math.PI / 180) + 0 * (2 * Math.PI / 5)) * 4 * 20 * scale;
  // ... other code
  // 连线
  ctx.beginPath();
  ctx.moveTo(point0_x, point0_y);
  ctx.lineTo(point1_x, point1_y);
  ctx.lineTo(point2_x, point2_y);
  ctx.lineTo(point3_x, point3_y);
  ctx.lineTo(point4_x, point4_y);
  ctx.closePath();
  ctx.stroke();
  ctx.fill(); // 填充
  
  if(scale < 1) {
    scale += 0.03;
    requestAnimationFrame(drawLine); // 动效
  }
}
drawLine();

// 清空画布
function clearCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
}

这里的 (point0_x, point0_y)point1_x, point1_y...点的坐标,对应上面👆 - 「五维度点设定」 计算出来的点坐标进行修改,追加了 scale 的参数。

然后,我们根据 scale 的大小进行动效操作,这里我们以 0.03 的倍数进行重绘。

动效切换

最后,我们就是进行动效的切换了:点击 - 我们得分 区域,对五个维度的值进行隐藏或展示。

首先,我们先将这个点击区域绘制出来,也就是画线和填充文本:

代码语言:javascript
复制
let showHightLight = true; // 展示五边形高亮

// 绘制 hint 线条和文本
function drawHintLine() {
  const _text = "我的得分";
  const _textWidth = ctx.measureText(_text).width;
  const _textOffsetLeft = 6;
  const _lineWidth = 20;
  const _offsetBottom = 20;
  ctx.fillStyle = "#fff";
  // 根据条件展示高亮线条颜色
  if(showHightLight) {
    ctx.strokeStyle = "#FDB452";
  } else {
    ctx.strokeStyle = "#fff";
  }
  ctx.textAlign = "left";
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.moveTo(canvas.width / 2 - (_textWidth + _lineWidth + _textOffsetLeft) / 2, canvas.height - _offsetBottom);
  ctx.lineTo(canvas.width / 2 - (_textWidth + _lineWidth + _textOffsetLeft) / 2 + _lineWidth, canvas.height - _offsetBottom);
  ctx.fillText(_text, canvas.width / 2 - (_textWidth + _lineWidth + _textOffsetLeft) / 2 + _lineWidth + _textOffsetLeft, canvas.height - _offsetBottom);
  ctx.stroke();
  ctx.closePath();
  ctx.lineWidth = 1;
  ctx.strokeStyle = "#fff";
}

接下来就是监听点击区域了,我们这里会使用到 mousemoveclick 的方法,结合 isHoverTextHint 变量:

代码语言:javascript
复制
let isHoverTextHint = false; // 是否鼠标在 我的分数提示上
// 监听鼠标移动
canvas.addEventListener("mousemove", function(event) {
  const _text = "我的得分";
  const _textWidth = ctx.measureText(_text).width; // 获取文本的宽度
  const _textHeight = ctx.measureText(_text).actualBoundingBoxAscent + ctx.measureText(_text).actualBoundingBoxDescent; // 获取文本的高度
  const _textOffsetLeft = 6;
  const _lineWidth = 20;
  const _offsetBottom = 20;
  // 获取鼠标在Canvas中的坐标
  var mouseX = event.clientX - canvas.offsetLeft;
  var mouseY = event.clientY - canvas.offsetTop;
  
  if(mouseX >= (canvas.width / 2 - (_textWidth + _textOffsetLeft + _lineWidth) / 2) && mouseX <= ((canvas.width / 2 - (_textWidth + _textOffsetLeft + _lineWidth) / 2) + _textWidth + _textOffsetLeft + _lineWidth) && mouseY >= (canvas.height - _textHeight - _offsetBottom) && mouseY <= (canvas.height + _textHeight)) {
    canvas.style.cursor = "pointer";
    isHoverTextHint = true;
  } else {
    canvas.style.cursor = "default";
    isHoverTextHint = false;
  }
})
// 监听鼠标滚动
canvas.addEventListener("click", function() {
  if(isHoverTextHint) {
    if(showHightLight) {
      clearCanvas();
      init();
      showHightLight = false;
      drawHintLine();
    } else {
      clearCanvas();
      init();
      scale = 0;
      drawPoints();
      drawLine();
      showHightLight = true;
    }
  }
})

当鼠标在 canvas 上移动的时候,我们监听。当鼠标位置在指定的位置区间,我们设置鼠标的样式为 canvas.style.cursor="pointer",否则为默认样式。

当鼠标在指定的位置进行点击的时候,我们就要进行效果的切换了。

Yeah,终于是无所不能的五边形战士了:

后话,当然,上面的代码,我并没有进行进一步的优化,如果读者感兴趣,可以根据步骤来实现,并进行相关的优化

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 灵感来源
  • 实现讲解
    • 五边形网格绘制
      • 五维度点设定
        • 五维度区域动效
          • 动效切换
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档