基于Canvas 六边形能力图(含动画)-干货满满~拿来就用!

在最近的工作中,有一个需求需要对个人进行多维度评分,UI需要我完成一个类似游戏中的能力六边形的绘制,如图:
六边形能力图

看我们如何手打一点点实现吧。
先贴完整代码:

<!DOCTYPE html>
<html>

<body>
    <!--定义画布容器,给个边框省的你们找不到-->
    <canvas id="myCanvas" width="400" height="400" style="border:1px solid #d3d3d3;">
        Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script>
        //获取画布对象
        var c = document.getElementById("myCanvas");
        var ctx = c.getContext("2d");
        //背景及同心圆相关参数,
        var canvaseSide = 400,//canvaseSide: 画布大小,从width、height获得
            arcMaxR = 180, //背景中最大同心圆的半径
            arcNum = 5,    //同心圆的个数
            arcOffset = arcMaxR/arcNum, //同心圆的半径差
            arcXPoint = canvaseSide/2,  //圆心x坐标
            arcYPoint = canvaseSide/2;  //圆心y坐标
        //六边形相关参数,
        var sideL = arcMaxR, //六边形边长(与最大同心圆半径同长,你们可以自己算下)
            yOffset = (Math.sqrt(3) * sideL) / 2, //y坐标的偏移量 cos 30° * 边长 
            xOffset = sideL / 2,  //x坐标的偏移量 sin 30° * 边长
            startY = canvaseSide/2, //定义point1 也就是起始点的坐标X
            startX = startY - arcMaxR;//定义point1 也就是起始点的坐标Y
        //定义六边形的6个点,点位置如下图所示
        var pointArray = [
            {"id":1, "x": startX, "y": startY },
            {"id":2, "x": startX + xOffset, "y": startY + yOffset }, 
            {"id":3, "x": startX + xOffset + sideL, "y": startY + yOffset },
            {"id":4, "x": startX + 2 * xOffset + sideL, "y": startY },
            {"id":5, "x": startX + xOffset + sideL, "y": startY - yOffset }, 
            {"id":6, "x": startX + xOffset, "y": startY - yOffset }
        ];
        //留个维度的得分
        var socreArray = [
            {"id":1,"score":80},
            {"id":2,"score":80},
            {"id":3,"score":80},
            {"id":4,"score":80},
            {"id":5,"score":80},
            {"id":6,"score":100},
        ];

        //计算分数以及每次更新的x坐标长度
        var maxIndex , minIndex;
        var drawScoreFrames = 24;
        var drawCount = 0;
        
        /*
        * 绘制背景图片
        */
        function drawBG(){
            //清空画布
            ctx.clearRect(0,0,c.width,c.height);
            ctx.beginPath();
            //绘制同心圆极其背景
            for (var i = arcNum; i >= 0; i--) {
                ctx.strokeStyle = "#E5EBEE";
                ctx.arc(arcXPoint, arcYPoint, i * arcOffset, 0, 2 * Math.PI);
                if (i == arcNum) {
                    ctx.fillStyle = "#F5FAFC";
                    ctx.fill();
                }
            }
            //绘制连线,画出1-4,2-5,3-6 六边形顶点的连线
            for(var i=0;i<3;i++){
                var startPoint = pointArray[i],
                    endPoint = pointArray[i+3];
                ctx.strokeStyle = "#E5EBEE";
                ctx.moveTo(startPoint.x,startPoint.y);
                ctx.lineTo(endPoint.x,endPoint.y);
                ctx.stroke();
            }

            //绘制六边形六个角的实心圆点
            for(var i=0;i<pointArray.length;i++){
                var point = pointArray[i];
                ctx.strokeStyle = "#2F8BF2";
                ctx.fillStyle = "#2F8BF2";
                ctx.beginPath();
                ctx.arc(point.x, point.y, 4, 0, 2 * Math.PI);
                ctx.fill();
                ctx.stroke();
            }
        }

        //计算分数以及每次更新的x坐标长度,主要思路,已固定的圆心坐标为参考系,定义到1,2,3,4,5,6点的线段上展示分数时每次的变化偏移量
        //这个变化变异量与 维度得分、哪条线段、总共渲染帧数有关
        for(var i=0;i<socreArray.length;i++){
            var scoreD = socreArray[i],
                scoreId = scoreD.id,
                scoreVal = scoreD.score;
            if(scoreId == 1){
                scoreD.xoffset = - (scoreVal* arcMaxR)/(100 * drawScoreFrames);
                scoreD.yoffset = 0;
            }
            if(scoreId == 2){
                scoreD.xoffset = - (scoreVal* arcMaxR)/(200 * drawScoreFrames);
                scoreD.yoffset = - (Math.sqrt(3)* scoreVal * arcMaxR)/(200 * drawScoreFrames);
            }
            if(scoreId == 3){
                scoreD.xoffset =  (scoreVal* arcMaxR)/(200 * drawScoreFrames);
                scoreD.yoffset = - (Math.sqrt(3)* scoreVal * arcMaxR)/(200 * drawScoreFrames);
                if(scoreVal > socreArray[1].score){
                    maxIndex = 2;
                }else{
                    maxIndex = 1;
                }
            }
            if(scoreId == 4){
                scoreD.xoffset =  (scoreVal* arcMaxR)/(100 * drawScoreFrames);
                scoreD.yoffset = 0;
            }
            if(scoreId == 5){
                scoreD.xoffset =  (scoreVal* arcMaxR)/(200 * drawScoreFrames);
                scoreD.yoffset =  (Math.sqrt(3)* scoreVal * arcMaxR)/(200 * drawScoreFrames);
            }
            if(scoreId == 6){
                scoreD.xoffset = - (scoreVal* arcMaxR)/(200 * drawScoreFrames);
                scoreD.yoffset =  (Math.sqrt(3)* scoreVal * arcMaxR)/(200 * drawScoreFrames);
                if(scoreVal > socreArray[4].score){
                    minIndex = 5;
                }else{
                    minIndex = 4;
                }
            }
        }

      
        /**
        * 动态渲染得分,思路如下
        * 1、每次清空上次的BG(同心圆)并重新绘制
        * 2、根据这是第几次渲染,计算圆心到1-6个点连线的坐标
        * 3、将坐标连接起来渲染成六边形,并定时开始下一次渲染实现动画
        */
        function dyncDrawSoce(){
            if(drawCount>drawScoreFrames){
                return;
            }
            drawBG();
            ctx.beginPath();
            var grdStartX ,grdStartY,grdEndX,grdEndy;

            for(var i=0;i<socreArray.length;i++){
                var scoreD = socreArray[i];
                if(i == 0){
                    ctx.moveTo(arcXPoint+scoreD.xoffset *drawCount, arcYPoint+scoreD.yoffset *drawCount);
                }else{
                    if(i == maxIndex){
                        grdStartX = arcXPoint+scoreD.xoffset *drawCount;
                        grdStartY =  arcYPoint+scoreD.yoffset *drawCount;
                    }
                    if(i == minIndex){
                        grdEndX = arcXPoint+scoreD.xoffset * drawCount;
                        grdEndy =  arcYPoint+scoreD.yoffset * drawCount;
                    }
                    ctx.lineTo(arcXPoint+scoreD.xoffset * drawCount, arcYPoint+scoreD.yoffset * drawCount);
                }
            }
            ctx.closePath();
            //TODO 这个地方绘制六边形
            //ctx.fill();
            ctx.strokeStyle = '#4C9CF6';
            var grd=ctx.createLinearGradient(grdStartX,grdStartY,grdEndX,grdEndy);
            grd.addColorStop(1,"rgb(122,190,241,0.4)");
            grd.addColorStop(0,"rgb(255,255,255,0.4)");
            ctx.fillStyle = grd;
            ctx.stroke();
            ctx.fill();
            drawCount++;

            setTimeout("dyncDrawSoce();",500/drawScoreFrames);

        }
        dyncDrawSoce();
        

    </script>

</body>

</html>
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当然可以!以下是一个使用 Canvas 生成六边形能力的方法: ```javascript function drawHexagonAbilityChart(canvas, abilities) { // 获取画布上下文 const ctx = canvas.getContext("2d"); // 设置六边形的参数 const hexagonRadius = 60; // 六边形半径 const centerX = canvas.width / 2; // 六边形中心点 x 坐标 const centerY = canvas.height / 2; // 六边形中心点 y 坐标 // 设置能力值的最大值和最小值 const maxValue = Math.max(...abilities); const minValue = Math.min(...abilities); // 设置能力的颜色 const fillColor = "rgba(0, 128, 255, 0.5)"; const strokeColor = "rgba(0, 128, 255, 1)"; // 绘制能力 ctx.beginPath(); for (let i = 0; i < abilities.length; i++) { const angle = (2 * Math.PI * i) / abilities.length - Math.PI / 2; const abilityValue = abilities[i]; // 计算能力值对应的半径 const radius = ((abilityValue - minValue) / (maxValue - minValue)) * hexagonRadius; // 计算六边形的顶点坐标 const x = centerX + radius * Math.cos(angle); const y = centerY + radius * Math.sin(angle); // 绘制六边形的边 if (i === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.closePath(); // 填充能力 ctx.fillStyle = fillColor; ctx.fill(); // 绘制能力的边框 ctx.strokeStyle = strokeColor; ctx.lineWidth = 2; ctx.stroke(); } ``` 这个方法接受两个参数:`canvas` 是要绘制的 Canvas 元素,`abilities` 是一个包各项能力值的数组。你可以根据实际情况传入不同的能力值数组来生成不同的能力。调用这个方法后,会在指定的 Canvas 元素上生成对应的六边形能力。 希望这个方法能够满足你的需求!如果有任何问题,请随时告诉我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值