日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

微信小程序canvas绘制环形图(含动画)

發布時間:2025/3/21 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 微信小程序canvas绘制环形图(含动画) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

頁面版

效果圖

思路

1、使用一個canvas繪制(帶動畫);
2、通過畫弧線,設置線寬,來實現圓環效果;
3、計算每段圓弧的起始角度和終止角度,用遞歸做動畫;
繪制完第一段圓弧塊–>再繪制下一塊,下一塊的起始角度是上一塊結束的角度+間隙(中間的白色分割線);
4、當最后一段圓弧塊繪制完成時,繪制延伸線+文字;
5、如下圖,通過三角函數確定延伸點A點位置,判斷延伸點位于中心點的左或右,繪制延伸線;
6、確定A點坐標:
7、已知A點到圓心的長度edge = 半徑r + 伸出去點的長度;
let edgeX = Math.cos(startAngle + angle / 2) * edge;
let edgeY = Math.sin(startAngle + angle / 2) * edge;
let outX = 圓心X + edgeX;
let outY = 圓心Y + edgeY;
A點坐標(outX,outY)

wxml

<canvas canvas-id="canvas" style="width:{{canvasW}}px;height:{{canvasH}}px;margin:auto;"></canvas>

js

const screenWidth = 360; //屏幕寬度,自己獲取 let pieInitData = { //環形餅圖默認初始數據mW: 0.9 * screenWidth / 2,mH: 0.6 * screenWidth / 2,r: 0.15 * screenWidth,lineW: 0.07 * screenWidth,chink: 2 * Math.PI / 180,/* 環形間距 */outSpot: 0.067 * screenWidth, //伸出去點的長度outLine: 0.1 * screenWidth, //伸出去線的長度signR: 0.008 * screenWidth, //點半徑fontSize: 0.03 * screenWidth, //字體大小textSpace: 0.025 * screenWidth, //文字上下與線的間距speed: 2 * Math.PI / 30, /* 速度 */moneyColorArr: ['#FF7573', '#7a95ff', '#0F8EE9', '#44d7b6', '#62D174', '#f2d510', '#FEBE3D', '#FFBE9B'] }; Page({data:{canvasW:0.9*screenWidth,canvasH:0.6*screenWidth},onLoad() {let data=[{ value:'6000-8000/月',ratio:17 },{ value:'4500-6000/月',ratio:34.8 },{ value:'3000-4500/月',ratio:36.4 },{ value:'8000-1w/月',ratio:3.9 },{ value:'1w-1.5w/月',ratio:5 },{ value:'2000-3000/月',ratio:2.2 }]this.drawPie('canvas',data)},// 環形餅圖drawPie(canvasId, data) {let ctx = wx.createCanvasContext(canvasId);ctx.clearRect(0, 0, pieInitData.mW * 2, pieInitData.mH * 2);let oldOutY = 0;let oldDir = 'right';drawRing(); //繪制圓環function drawRing() {let all = 0;for (let i = 0; i < data.length; i++) {all += data[i].ratio}let angleList = transformAngle();let angleArr = [];let pieIndex = 0;let startAngle = 3 / 2 * Math.PI;loop(pieIndex)function loop(index) {let endAngle = startAngle + angleList[index].angle;ctx.beginPath();let proportion = 0;for (let j = 0; j < index; j++) {proportion += data[j].ratio;};let start = 3 / 2 * Math.PI + 2 * Math.PI * proportion / all;let end = start;pieAnimate(index, end, start);angleArr.push({ startAngle: startAngle, angle: angleList[index].angle })startAngle = endAngle;}/*** index 第幾個圓弧塊* end 結束的角度* start 開始的角度*/function pieAnimate(index, end, start) {setTimeout(() => {let endLimit = start + 2 * Math.PI * data[index].ratio / all - pieInitData.chink;if (end < endLimit) {end += pieInitData.speed;if (end > endLimit) {end = endLimit}pieAnimate(index, end, start);} else {if (pieIndex < data.length - 1) {pieIndex++;loop(pieIndex)} else {// 當最后一個圓弧angleArr.forEach(function (item, i) {drawArcLine(item.startAngle, item.angle, i);//繪制點線});}}}, 10)ctx.setLineWidth(pieInitData.lineW);ctx.setStrokeStyle(pieInitData.moneyColorArr[pieIndex]);ctx.arc(pieInitData.mW, pieInitData.mH, pieInitData.r, start, end);ctx.stroke();ctx.draw(true);}// 轉化弧度function transformAngle() {let total = 0;data.forEach(function (item, i) {total += item.ratio;});data.forEach(function (item, i) {var angle = item.ratio / total * Math.PI * 2;item.angle = angle;});return data;};/*** startAngle 圓弧塊開始的角度* angle 圓弧塊扇形的角度*/function drawArcLine(startAngle, angle, index) {/*計算點出去的坐標*/let edge = pieInitData.r + pieInitData.outSpot;let edgeX = Math.cos(startAngle + angle / 2) * edge;let edgeY = Math.sin(startAngle + angle / 2) * edge;let outX = pieInitData.mW + edgeX;let outY = pieInitData.mH + edgeY;/*計算線出去的坐標*/let edge1 = pieInitData.r + pieInitData.outLine;let edgeX1 = Math.cos(startAngle + angle / 2) * edge1;let edgeY1 = Math.sin(startAngle + angle / 2) * edge1;let outX1 = pieInitData.mW + edgeX1;let outY1 = pieInitData.mH + edgeY1;ctx.beginPath();let dir = 'right';if (outX1 > pieInitData.mW) {dir = 'right';} else {dir = 'left';}ctx.setStrokeStyle(pieInitData.moneyColorArr[index]);ctx.setLineWidth(1);ctx.setFontSize(pieInitData.fontSize);ctx.setTextBaseline('middle');if (Math.abs(outY - oldOutY) > 10 || dir != oldDir) { ctx.arc(outX - pieInitData.signR / 2, outY - pieInitData.signR / 2, pieInitData.signR, 0, 2 * Math.PI); }ctx.setFillStyle(pieInitData.moneyColorArr[index]);ctx.fill();ctx.moveTo(outX - pieInitData.signR / 2, outY - pieInitData.signR / 2);ctx.lineTo(outX1, outY1);/*** 優化,* 上下距離大于30時,上下顯示* 上下距離大于10,小于30時,一行顯示 3.9% 8000-1w/月 為一行* 否則不顯示*/if (Math.abs(outY - oldOutY) > 30 || dir != oldDir) {oldOutY = outY;oldDir = dir;if (dir == 'right') {/*右*/ctx.lineTo(pieInitData.mW * 2, outY1);ctx.stroke();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('left');const rightValueW = ctx.measureText(data[index].value).width;const rightRatioW = ctx.measureText(data[index].ratio + '%').width;ctx.fillText(data[index].value, pieInitData.mW * 2 - rightValueW, outY1 + pieInitData.textSpace);ctx.fillText(data[index].ratio + '%', pieInitData.mW * 2 - rightRatioW, outY1 - pieInitData.textSpace);} else {/*左*/ctx.lineTo(0, outY1);ctx.stroke();ctx.beginPath();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('right');const leftValueW = ctx.measureText(data[index].value).width;const leftRatioW = ctx.measureText(data[index].ratio + '%').width;ctx.fillText(data[index].value, 0 + leftValueW, outY1 + pieInitData.textSpace);ctx.fillText(data[index].ratio + '%', 0 + leftRatioW, outY1 - pieInitData.textSpace);}} else {if (Math.abs(outY - oldOutY) >= 10) {oldOutY = outY;oldDir = dir;if (dir == 'right') {/*右*/const lineOffsetR = ctx.measureText('1000%').width;ctx.lineTo(pieInitData.mW * 2 - lineOffsetR, outY1);ctx.stroke();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('left');const rightRatioW = ctx.measureText(data[index].ratio + '% ' + data[index].value + '1000%').width;ctx.fillText(data[index].ratio + '% ' + data[index].value, pieInitData.mW * 2 - rightRatioW, outY1 + pieInitData.textSpace);} else {/*左*/const lineOffsetL = ctx.measureText('1000%').width;ctx.lineTo(0 + lineOffsetL, outY1);ctx.stroke();ctx.beginPath();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('right');const leftRatioW = ctx.measureText(data[index].ratio + '% ' + data[index].value + '1000%').width;ctx.fillText(data[index].ratio + '% ' + data[index].value, 0 + leftRatioW, outY1 - pieInitData.textSpace);}}}ctx.draw(true);}}} })

組件版

components

circle.wxml

<canvas canvas-id="canvas" style="width:{{canvasW}}px;height:{{canvasH}}px;margin:auto;"></canvas>

circle.js

const screenWidth = 360; //屏幕寬度,自己獲取 let pieInitData = { //環形餅圖默認初始數據mW: 0.9 * screenWidth / 2,mH: 0.6 * screenWidth / 2,r: 0.15 * screenWidth,lineW: 0.07 * screenWidth,chink: 2 * Math.PI / 180,/* 環形間距 */outSpot: 0.067 * screenWidth, //伸出去點的長度outLine: 0.1 * screenWidth, //伸出去線的長度signR: 0.008 * screenWidth, //點半徑fontSize: 0.03 * screenWidth, //字體大小textSpace: 0.025 * screenWidth, //文字上下與線的間距speed: 2 * Math.PI / 30,/* 速度 */moneyColorArr: ['#FF7573', '#7a95ff', '#0F8EE9'] }; Component({data: {canvasW: 0.9 * screenWidth,canvasH: 0.6 * screenWidth},pageLifetimes: {show: function() {let optionData = [{value: '6000-8000/月',ratio: 17},{value: '4500-6000/月',ratio: 34.8},{value: '3000-4500/月',ratio: 36.4}]this.drawPie( optionData)},},methods: {// 環形餅圖drawPie(data) {let ctx = wx.createCanvasContext('canvas',this);ctx.clearRect(0, 0, pieInitData.mW * 2, pieInitData.mH * 2);let oldOutY = 0;let oldDir = 'right';drawRing(); //繪制圓環function drawRing() {let all = 0;for (let i = 0; i < data.length; i++) {all += data[i].ratio}let angleList = transformAngle();let angleArr = [];let pieIndex = 0;let startAngle = 3 / 2 * Math.PI;loop(pieIndex)function loop(index) {let endAngle = startAngle + angleList[index].angle;ctx.beginPath();let proportion = 0;for (let j = 0; j < index; j++) {proportion += data[j].ratio;};let start = 3 / 2 * Math.PI + 2 * Math.PI * proportion / all;let end = start;pieAnimate(index, end, start);angleArr.push({startAngle: startAngle,angle: angleList[index].angle})startAngle = endAngle;}/*** index 第幾個圓弧塊* end 結束的角度* start 開始的角度*/function pieAnimate(index, end, start) {console.log('=======組件pieAnimate======')setTimeout(() => {let endLimit = start + 2 * Math.PI * data[index].ratio / all - pieInitData.chink;if (end < endLimit) {end += pieInitData.speed;if (end > endLimit) {end = endLimit}pieAnimate(index, end, start);} else {if (pieIndex < data.length - 1) {pieIndex++;loop(pieIndex)} else {// 當最后一個圓弧angleArr.forEach(function (item, i) {drawArcLine(item.startAngle, item.angle, i); //繪制點線});}}}, 10)ctx.setLineWidth(pieInitData.lineW);ctx.setStrokeStyle(pieInitData.moneyColorArr[pieIndex]);ctx.arc(pieInitData.mW, pieInitData.mH, pieInitData.r, start, end);ctx.stroke();ctx.draw(true);}// 轉化弧度function transformAngle() {console.log('=======組件transformAngle======')let total = 0;data.forEach(function (item, i) {total += item.ratio;});data.forEach(function (item, i) {var angle = item.ratio / total * Math.PI * 2;item.angle = angle;});return data;};/*** startAngle 圓弧塊開始的角度* angle 圓弧塊扇形的角度*/function drawArcLine(startAngle, angle, index) {console.log('=======組件drawArcLine======')/*計算點出去的坐標*/let edge = pieInitData.r + pieInitData.outSpot;let edgeX = Math.cos(startAngle + angle / 2) * edge;let edgeY = Math.sin(startAngle + angle / 2) * edge;let outX = pieInitData.mW + edgeX;let outY = pieInitData.mH + edgeY;/*計算線出去的坐標*/let edge1 = pieInitData.r + pieInitData.outLine;let edgeX1 = Math.cos(startAngle + angle / 2) * edge1;let edgeY1 = Math.sin(startAngle + angle / 2) * edge1;let outX1 = pieInitData.mW + edgeX1;let outY1 = pieInitData.mH + edgeY1;ctx.beginPath();let dir = 'right';if (outX1 > pieInitData.mW) {dir = 'right';} else {dir = 'left';}ctx.setStrokeStyle(pieInitData.moneyColorArr[index]);ctx.setLineWidth(1);ctx.setFontSize(pieInitData.fontSize);ctx.setTextBaseline('middle');if (Math.abs(outY - oldOutY) > 10 || dir != oldDir) {ctx.arc(outX - pieInitData.signR / 2, outY - pieInitData.signR / 2, pieInitData.signR, 0, 2 * Math.PI);}ctx.setFillStyle(pieInitData.moneyColorArr[index]);ctx.fill();ctx.moveTo(outX - pieInitData.signR / 2, outY - pieInitData.signR / 2);ctx.lineTo(outX1, outY1);/*** 優化,* 上下距離大于30時,上下顯示* 上下距離大于10,小于30時,一行顯示 3.9% 8000-1w/月 為一行* 否則不顯示*/if (Math.abs(outY - oldOutY) > 30 || dir != oldDir) {oldOutY = outY;oldDir = dir;if (dir == 'right') {/*右*/ctx.lineTo(pieInitData.mW * 2, outY1);ctx.stroke();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('left');const rightValueW = ctx.measureText(data[index].value).width;const rightRatioW = ctx.measureText(data[index].ratio + '%').width;ctx.fillText(data[index].value, pieInitData.mW * 2 - rightValueW, outY1 + pieInitData.textSpace);ctx.fillText(data[index].ratio + '%', pieInitData.mW * 2 - rightRatioW, outY1 - pieInitData.textSpace);} else {/*左*/ctx.lineTo(0, outY1);ctx.stroke();ctx.beginPath();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('right');const leftValueW = ctx.measureText(data[index].value).width;const leftRatioW = ctx.measureText(data[index].ratio + '%').width;ctx.fillText(data[index].value, 0 + leftValueW, outY1 + pieInitData.textSpace);ctx.fillText(data[index].ratio + '%', 0 + leftRatioW, outY1 - pieInitData.textSpace);}} else {if (Math.abs(outY - oldOutY) >= 10) {oldOutY = outY;oldDir = dir;if (dir == 'right') {/*右*/const lineOffsetR = ctx.measureText('1000%').width;ctx.lineTo(pieInitData.mW * 2 - lineOffsetR, outY1);ctx.stroke();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('left');const rightRatioW = ctx.measureText(data[index].ratio + '% ' + data[index].value + '1000%').width;ctx.fillText(data[index].ratio + '% ' + data[index].value, pieInitData.mW * 2 - rightRatioW, outY1 + pieInitData.textSpace);} else {/*左*/const lineOffsetL = ctx.measureText('1000%').width;ctx.lineTo(0 + lineOffsetL, outY1);ctx.stroke();ctx.beginPath();ctx.setFillStyle('#4a4a4a');ctx.setTextAlign('right');const leftRatioW = ctx.measureText(data[index].ratio + '% ' + data[index].value + '1000%').width;ctx.fillText(data[index].ratio + '% ' + data[index].value, 0 + leftRatioW, outY1 - pieInitData.textSpace);}}}ctx.draw(true);}}}} })

circle.json

{"component": true,"usingComponents": {"circle": "./circle"} }

引入頁面

wxml

<view class="progress-box"><circle></circle> </view>

json

{"usingComponents": {"circle": "../../components/circle/circle"},"navigationBarTitleText": "" }

總結

以上是生活随笔為你收集整理的微信小程序canvas绘制环形图(含动画)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。