- A+
最近收到一个请求,要根据用户6个项目的完成情况显示1、2、3、4、5、6个正六边形,但后续要每条边都动画化并引导到每个角。
大概疗效如图所示:
这时候我就想有什么办法可以更快的实现呢?
计算六边形点的位置
无论我们使用canvas还是svg,我们仍然需要知道绘制点的位置,所以我们第一步就是询问每个点的位置(我不会告诉你svg画六边形,这篇文章的主要目的是记住这个估计... )
由于这里使用的都是正n边形,因此可以根据角度和直径计算出每个点的位置
123456789101112131415161718192021
let len = 6;// 正多边形的点位置let points = [];// 内角角度let angle = 360 / len;// 半径let radio = 100;// 控制方向,i从0-len算,顺时针绘制,i从len-0,逆时针绘制for (let i = len; i > 0; i--) {// 270是控制初始点位置var x = Math.cos(((i * angle + 270) / 180) * Math.PI);var y = Math.sin(((i * angle + 270) / 180) * Math.PI);// 如果只有1个时,放圆心if (len === 1) {x = 0;y = 0;}let sx = x * radio + radio;let sy = y * radio + radio;points.push(sx, sy);}
轮廓六边形svg绘制方法 轮廓五边形
svg中有一个polygon标签可以绘制五边形
1234567891011121314151617181920212223
<svgwidth="100%"height="100%"version="1.1"xmlns="http://www.w3.org/2000/svg"viewBox="0 0 200 200"><polygonpoints="100 013.397 5013.397 150100 200186.602 150186.602 50"stroke="#242d50"fill="transparent"stroke-width="2"stroke-linejoin="miter"stroke-linecap="butt"/></svg>
勾勒出六边形的渐变单线来定义渐变元素
12345678910111213
<defs><linearGradient id="Gradient1" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#c25813" /><stop offset="50%" stop-color="#c28112" /><stop offset="100%" stop-color="#caac11" /></linearGradient><filter id="f1" x="0" y="0" width="100%" height="100%"><feGaussianBlur result="blurOut" in="offOut" stdDeviation="2" /></filter></defs>
如右图所示,垂直线外圆的宽度为0svg画六边形,这样就无法绘制渐变线,但非垂直线可以
右图中,左图是gradientUnits="userSpaceOnUse",可以绘制垂直渐变线段,但是这里的渐变颜色范围是整个svg而不是单个线段(导致每条线段的渐变不一致);
下图是gradientUnits="objectBoundingBox",它绘制的渐变线段是一致的,但是无法绘制垂直线段。
定义单线并使用渐变元素
123456789101112131415161718192021
<g class="active"><linex1="100"x2="13.397"y1="0"y2="50"stroke="#535244"stroke-width="4"filter="url(#f1)"/><linex1="100"x2="13.397"y1="0"y2="50"stroke="url(#Gradient1)"stroke-width="2"/></g>
单行动漫
使用strike-dasharray和css3动画来实现动画。 当需要动画的时候,给对应的元素添加active类,这样就可以显示动画了
1234567891011121314151617
.active line {fill: transparent;stroke-dasharray: 100px;animation-name: outline;animation-duration: 0.5s;animation-timing-function: ease-in-out;animation-fill-mode: both;}@keyframes outline {0% {stroke-dashoffset: 100px;}100% {stroke-dashoffset: 0;}}
canvas绘制方法绘制六边形
1234567891011121314151617181920212223242526272829303132333435363738394041424344
let canvas = document.getElementById('polyline');let devicePixelRatio = window.devicePixelRatio;let width = Math.min(canvas.width, canvas.height);// 考虑高清屏canvas.width = width * devicePixelRatio;canvas.height = width * devicePixelRatio;// 计算绘制的多边形的半径let radio = canvas.width / 2;let len = 6;let angle = 360 / len;let points = [];for (let i = len; i > 0; i--) {// 270 控制初始点位置var x = Math.cos(((i * angle + 270) / 180) * Math.PI);var y = Math.sin(((i * angle + 270) / 180) * Math.PI);// 如果只有1个时,放圆心if (len === 1) {x = 0;y = 0;}let sx = x * radio + radio;let sy = y * radio + radio;points.push(sx, sy);}let ctx = canvas.getContext('2d');// 绘制正六边形ctx.beginPath();for (let i = 0; i < points.length; i += 2) {let fn = 'lineTo';if (i === 0) {fn = 'moveTo';}console.log(points[i], points[i + 1]);ctx[fn](points[i], points[i + 1]);}ctx.closePath();ctx.lineWidth = 2 * devicePixelRatio;ctx.strokeStyle = '#242d50';ctx.stroke();// 将点闭合points.push(points[0], points[1]);// 动画绘制单线animateDrawLine(canvas, points[0], points[1], points[2], points[3]);
渐变单线勾勒出六边形
123456789101112131415161718192021
// 绘制渐变色单线for (let i = 0; i < points.length - 2; i += 2) {// 底线虚化,使用shadowBlur 属性实现ctx.shadowBlur = 6;ctx.shadowColor = '#535244';//由于每条线的渐变位置不同,所以需每次都重新定义let gradient = ctx.createLinearGradient(points[i],points[i + 1],points[i + 2],points[i + 3]);gradient.addColorStop('0', '#caac11');gradient.addColorStop('0.5', '#c28112');gradient.addColorStop('1.0', '#c25813');ctx.strokeStyle = gradient;ctx.beginPath();ctx.moveTo(points[i], points[i + 1]);ctx.lineTo(points[i + 2], points[i + 3]);ctx.stroke();}
单行动漫
画布的动画主要使用requestAnimationFrame函数来绘制每一帧。 这里的animate是一个自己封装的动画类,它给出开始状态、结束状态和持续时间,在onTick函数中估计中间状态,然后自己处理。
1234567891011121314151617181920212223242526272829303132333435363738394041424344
function animateDrawLine(canvas, x1, y1, x2, y2, duration = 500) {console.log(x1, y1, x2, y2);let ctx = canvas.getContext('2d');let gradient = ctx.createLinearGradient(x1, y1, x2, y2);gradient.addColorStop('0', '#caac11');gradient.addColorStop('0.5', '#c28112');gradient.addColorStop('1.0', '#c25813');let lastX = x1;let lastY = y1;new Animate({start: {x: x1,y: y1},end: {x: x2,y: y2},during: duration,onTick: function(value) {let { x, y } = value;// 由于这里没有clear掉之前画的内容,如果用showdow的话,上一帧绘制的阴影会影响下一帧的// ctx.shadowBlur=6;// ctx.shadowColor="#535244";// 改用filter只绘制当前帧的小段距离ctx.filter = 'blur(6px)';ctx.strokeStyle = '#535244';ctx.beginPath();ctx.moveTo(lastX, lastY);ctx.lineTo(x, y);ctx.stroke();// 如果下面的线也是只画当前帧的小段距离的话,会有间隙ctx.filter = 'none';ctx.strokeStyle = gradient;ctx.beginPath();ctx.moveTo(x1, y1);ctx.lineTo(x, y);ctx.stroke();lastX = x;lastY = y;}});}
异同比较
svg/canvas 轮廓正多边形演示
- 我的微信
- 这是我的微信扫一扫
-
- 我的微信公众号
- 我的微信公众号扫一扫
-