index.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. class Gauge {
  2. constructor({
  3. canvasId,
  4. startAngle = 3 / 4 * Math.PI, // 135
  5. endAngle = 1 / 4 * Math.PI, // 45
  6. width = 200,
  7. min = 0,
  8. max = 100,
  9. value = 0,
  10. unit = '%',
  11. showTick = false,
  12. valueColor = '#555',
  13. progressColor = '#f58220',
  14. trackColor = '#F1F1F1',
  15. lineWidth = 10
  16. }) {
  17. this.ctx = uni.createCanvasContext(canvasId)
  18. this.startAngle = startAngle
  19. this.endAngle = endAngle
  20. this.width = width
  21. this.min = min
  22. this.max = max
  23. this.value = value
  24. this.unit = unit
  25. this.showTick = showTick
  26. this.valueColor = valueColor
  27. this.progressColor = progressColor
  28. this.trackColor = trackColor
  29. // track width
  30. this.lineWidth = lineWidth
  31. this.radius = this.width / 2
  32. this.process = min || -1
  33. this.tickLength = this.max - this.min
  34. // track radius
  35. this.trackRadius = this.radius - this.lineWidth / 2
  36. // one tick rad
  37. this.tickRad = (2 * Math.PI - this.startAngle + this.endAngle) / this.tickLength
  38. this.start()
  39. }
  40. draw() {
  41. // 将坐标中心点移动到圆心位置
  42. this.ctx.translate(this.radius, this.radius)
  43. this.drawTrack()
  44. this.drawValueText()
  45. this.drawProgress()
  46. if (this.showTick) {
  47. this.drawTick()
  48. this.drawTickText()
  49. }
  50. this.ctx.draw()
  51. }
  52. // 轨迹圆环
  53. drawTrack() {
  54. this.ctx.save()
  55. this.ctx.lineWidth = this.lineWidth
  56. // this.ctx.lineCap = 'round'
  57. this.ctx.strokeStyle = this.trackColor
  58. this.ctx.arc(0, 0, this.trackRadius, this.startAngle, this.endAngle)
  59. this.ctx.stroke()
  60. this.ctx.restore()
  61. }
  62. // 当前值显示
  63. drawValueText() {
  64. this.ctx.save()
  65. this.ctx.fillStyle = this.valueColor
  66. this.ctx.font = "34px serif"
  67. this.ctx.textAlign = 'center'
  68. this.ctx.textBaseline = 'middle'
  69. this.ctx.fillText(`${this.value}${this.unit}`, 0, 0)
  70. this.ctx.restore()
  71. }
  72. // 进度圆环
  73. drawProgress() {
  74. this.ctx.save()
  75. const valueRad = this.getValueRad()
  76. this.ctx.beginPath()
  77. this.ctx.lineWidth = this.lineWidth
  78. this.ctx.strokeStyle = typeof this.progressColor === 'string' ? this.progressColor : this.getGradient(this.progressColor)
  79. // this.ctx.lineCap = 'round' // 会导致进度条加长
  80. this.ctx.lineJoin = 'round'
  81. // 终点角度是动态的
  82. this.ctx.arc(0, 0, this.trackRadius, this.startAngle, valueRad)
  83. this.ctx.stroke()
  84. this.ctx.restore()
  85. }
  86. // 刻度线
  87. drawTick() {
  88. // 不可是使用旋转,否则文字也会旋转
  89. let deg = this.startAngle
  90. for (let i = this.min; i <= this.max; i++) {
  91. this.ctx.save()
  92. this.ctx.lineWidth = 1
  93. this.ctx.strokeStyle = '#999'
  94. this.ctx.beginPath()
  95. // 刻度起点
  96. let x0 = (this.radius - this.lineWidth - 2) * Math.cos(deg)
  97. let y0 = (this.radius - this.lineWidth - 2) * Math.sin(deg)
  98. // 刻度终点
  99. var x1 = (this.radius - this.lineWidth - 8) * Math.cos(deg)
  100. var y1 = (this.radius - this.lineWidth - 8) * Math.sin(deg)
  101. this.ctx.moveTo(x0, y0)
  102. this.ctx.lineTo(x1, y1)
  103. deg += this.tickRad
  104. this.ctx.stroke()
  105. this.ctx.restore()
  106. }
  107. }
  108. // 刻度值
  109. drawTickText() {
  110. let deg = this.startAngle
  111. for (let i = this.min; i <= this.max; i++) {
  112. let x = (this.radius - this.lineWidth - 20) * Math.cos(deg)
  113. let y = (this.radius - this.lineWidth - 20) * Math.sin(deg)
  114. deg += this.tickRad
  115. this.ctx.save()
  116. this.ctx.font = '12px serif'
  117. this.ctx.fillStyle = '#999'
  118. this.ctx.textAlign = 'center'
  119. // 不设置会与tick错位
  120. this.ctx.textBaseline = 'middle'
  121. this.ctx.fillText(i, x, y)
  122. this.ctx.restore()
  123. }
  124. }
  125. // 当前值的弧度
  126. getValueRad() {
  127. // 超出
  128. if (this.value > this.max) {
  129. return (this.max - this.min) * this.tickRad + this.startAngle
  130. }
  131. return (this.process - this.min) * this.tickRad + this.startAngle
  132. }
  133. start() {
  134. let update = () => {
  135. this.process = this.process + (this.max - this.min) / 100
  136. if (this.process > this.value) {
  137. cancelAnimationFrame(update)
  138. } else {
  139. this.draw()
  140. requestAnimationFrame(update)
  141. }
  142. }
  143. requestAnimationFrame(update)
  144. }
  145. // 渐变
  146. getGradient(colorList) {
  147. let grd = this.ctx.createLinearGradient(0, 0, this.trackRadius, 0)
  148. colorList.forEach((color, index) => {
  149. grd.addColorStop(index / (colorList.length - 1), color)
  150. })
  151. return grd
  152. }
  153. }
  154. // https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
  155. (function () {
  156. var lastTime = 0;
  157. var vendors = ['webkit', 'moz'];
  158. for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  159. window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
  160. window.cancelAnimationFrame =
  161. window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
  162. }
  163. if (!window.requestAnimationFrame)
  164. window.requestAnimationFrame = function (callback, element) {
  165. var currTime = new Date().getTime();
  166. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  167. var id = window.setTimeout(function () { callback(currTime + timeToCall); },
  168. timeToCall);
  169. lastTime = currTime + timeToCall;
  170. return id;
  171. };
  172. if (!window.cancelAnimationFrame)
  173. window.cancelAnimationFrame = function (id) {
  174. clearTimeout(id);
  175. };
  176. }());
  177. export default Gauge