在使用canvas的时候发现数值变化,每次都要重新渲染,值都从0开始,这和我的需求冲突。
一、 纯JS+Vue
1. 先绘制基本的圆环背景,利用border-color
和border-radius
将正方形变成基本的圆环。
| <div class="circle"> |
| <div class="Inner"></div> |
| </div> |
复制
| .circle { |
| position: relative; |
| border-radius: 50%; |
| border: 12px solid; |
| border-color: green green transparent green; |
| width: 480px; |
| height: 484px; |
| top: 4%; |
| left: 12%; |
| } |
复制

利用border-radius
,就可将正方形变成圆形

2. 背景绘制完成,下面就是每个刻度。
| <div class="circle"> |
| <div class="Inner"></div> |
| <div class="center"></div> |
| <div class="pointer"></div> |
| </div> |
复制
| .center{ |
| width: 20px; |
| height: 20px; |
| background-color: grey; |
| border-radius: 50%; |
| position: absolute; |
| z-index: 35; |
| top: calc(50% - 5px); |
| left: calc(50% - 5px); |
| } |
| .pointer{ |
| width: 190px; |
| height:10px; |
| background-color: red; |
| border-radius: 50%; |
| position: absolute; |
| z-index: 34; |
| top: calc(50% - -2px); |
| left: calc(50% - 6px); |
| transform-origin:5% 35%; |
| } |
| .number { |
| color: #fff; |
| display: block; |
| padding-top: 16px; |
| position: absolute; |
| left: -6px; |
| } |
复制
一共100个值,每两个刻度就要有线,到10线的长度会更长一点。其实和画钟表一样,0的位置是坐标轴的225°,到100的位置,总共是180°+45°
| mounted() { |
| let circle = document.getElementsByClassName('Inner')[0]; |
| circle.style.setProperty('--width', Math.floor(227) + 'px'); |
| for (let i = 0; i <= 50; i++) { |
| const ul = document.createElement('ul'); |
| const li = document.createElement('li'); |
| li.style.transform = `rotate(${225 + i * 2 * 2.7}deg)`; |
| if (i % 5 === 0) { |
| li.style.height = '15px'; |
| li.innerHTML = `<span class = 'number'>${i*2}</span>` |
| } |
| circle?.appendChild(ul); |
| ul.appendChild(li); |
| } |
| } |
复制

3. 让指针根据数据变动,和刻度一样,每移动一个点要更改相应的刻度
| <div class="circle"> |
| <div class="Inner"></div> |
| <div class="center"></div> |
| <div class="pointer"></div> |
| <div class="num">{{dataValue}}%</div> |
| </div> |
| |
| <script> |
| export default { |
| data() { |
| return { |
| dataValue: 50, |
| } |
| }, |
| watch: { |
| dataValue: { |
| handler(newValue, oldVal) { |
| this.dataValue = newValue; |
| this.runGuage() |
| }, |
| }, |
| }, |
| methods: { |
| runGuage() { |
| let pointer = document.getElementsByClassName("pointer")[0]; |
| pointer.style.transform = `rotate(${137 + this.dataValue * 2.66}deg)`; |
| }, |
| }, |
| mounted() { |
| this.runGuage(); |
| } |
| } |
| </script> |
| |
| <style scoped> |
| .num{ |
| position: absolute; |
| color: #fff; |
| font-size: 70px; |
| z-index: 32; |
| top: 54%; |
| left: 30%; |
| } |
| </style> |
复制

二、 Canvas
静下心实现了canvas描绘的仪表盘,并且数据不会从0开始渲染。
1. 先来canvas的老三样
| <canvas id="progress" width="600" height="600"></canvas> |
| |
| <script> |
| |
| var canvas = document.getElementById("progress"); |
| var ctx = canvas.getContext("2d"); |
| </script> |
复制
2. 设置一些基本参数
| <canvas id="progress" width="600" height="600"></canvas> |
| |
| <script> |
| |
| var canvas = document.getElementById("progress"); |
| var ctx = canvas.getContext("2d"); |
| |
| |
| var x = canvas.width / 2; |
| var y = canvas.height / 2; |
| var radius = 176; |
| var lineWidth = 10; |
| var startAngle = (3 * Math.PI) / 4; |
| var endAngle = -Math.PI / 4; |
| </script> |
复制
3. 绘制圆环及外面的进度条
| |
| function drawProgress(progress) { |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| ctx.beginPath(); |
| ctx.arc(x, y, radius, startAngle, Math.PI / 4); |
| ctx.lineWidth = lineWidth; |
| ctx.strokeStyle = "#008000"; |
| ctx.stroke(); |
| |
| |
| endAngle = startAngle + ((3 * Math.PI / 2) * progress); |
| ctx.beginPath(); |
| ctx.arc(x, y, radius, startAngle, endAngle); |
| ctx.lineWidth = lineWidth; |
| ctx.strokeStyle = "#ff0000"; |
| ctx.stroke(); |
| } |
| |
| drawProgress(0.2) |
复制

4. 绘制里面的刻度条
| function drawTicks() { |
| var numTicks = 50; |
| var tickLength = 8; |
| var tickColor = "#000000"; |
| var tickWidth = 2; |
| |
| let startAngle = (3 * Math.PI) / 4; |
| let endAngle = -(1.48 * Math.PI) / 2; |
| |
| var angleStep = -((endAngle - startAngle) / numTicks); |
| |
| ctx.save(); |
| |
| |
| ctx.translate(x, y); |
| |
| |
| |
| for (var i = 0; i <= numTicks; i++) { |
| var angle = startAngle + (i * angleStep) + 0.01; |
| if (i % 5 == 0) { |
| tickWidth = 5; |
| } else { |
| tickWidth = 2; |
| } |
| ctx.beginPath(); |
| ctx.lineWidth = tickWidth; |
| ctx.strokeStyle = tickColor; |
| |
| |
| var startX = (radius - tickLength - 4) * Math.cos(angle); |
| var startY = (radius - tickLength - 4) * Math.sin(angle); |
| var endX = (radius - 4) * Math.cos(angle); |
| var endY = (radius - 4) * Math.sin(angle); |
| |
| |
| |
| ctx.moveTo(startX, startY); |
| ctx.lineTo(endX, endY); |
| ctx.stroke(); |
| |
| |
| var text = i % 5 == 0 ? (i * 2).toString() : ''; |
| var textX = (radius - tickLength - 20) * Math.cos(angle); |
| var textY = (radius - tickLength - 20) * Math.sin(angle); |
| ctx.font = "16px Arial"; |
| ctx.fillStyle = "#000000"; |
| ctx.textAlign = "center"; |
| ctx.textBaseline = "middle"; |
| ctx.fillText(text, textX, textY); |
| } |
| |
| ctx.restore(); |
| } |
复制

5. 绘制指针
这一段可以抽离出来一个方法,等有时间再改一下
| let kedu = (((2.4) * 2) / 100); |
| let rotate1 = (2.4 - (kedu * progress * 100)); |
| if (progress < 0.5) { |
| rotate1 = -rotate1; |
| } else { |
| rotate1 = -rotate1; |
| } |
| ctx.save(); |
| ctx.translate(x, y); |
| ctx.rotate(rotate1); |
| ctx.beginPath(); |
| ctx.moveTo(x - (2 * radius) + 50, y - (2 * radius) + 60); |
| ctx.lineTo(x - (2 * radius) + 40, y - (2 * radius) + 46); |
| ctx.lineTo(x - (2 * radius) + 50, y - (2 * radius) - 70); |
| ctx.lineTo(x - (2 * radius) + 60, y - (2 * radius) + 46); |
| ctx.fillStyle = "#ff0000"; |
| ctx.fill(); |
| ctx.restore(); |
复制

6. 绘制文本显示当前的值
| function drawText(value){ |
| ctx.font = "40px Arial"; |
| ctx.fillStyle = "#000000"; |
| ctx.fillText((value*100).toFixed(2)+'%', x-60, y+42); |
| } |
复制

完成~