index.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { VantComponent } from '../common/component';
  2. import { BLUE, WHITE } from '../common/color';
  3. import { adaptor } from './canvas';
  4. import { isObj } from '../common/validator';
  5. import { getSystemInfoSync } from '../common/utils';
  6. function format(rate) {
  7. return Math.min(Math.max(rate, 0), 100);
  8. }
  9. const PERIMETER = 2 * Math.PI;
  10. const BEGIN_ANGLE = -Math.PI / 2;
  11. const STEP = 1;
  12. VantComponent({
  13. props: {
  14. text: String,
  15. lineCap: {
  16. type: String,
  17. value: 'round',
  18. },
  19. value: {
  20. type: Number,
  21. value: 0,
  22. observer: 'reRender',
  23. },
  24. speed: {
  25. type: Number,
  26. value: 50,
  27. },
  28. size: {
  29. type: Number,
  30. value: 100,
  31. observer() {
  32. this.drawCircle(this.currentValue);
  33. },
  34. },
  35. fill: String,
  36. layerColor: {
  37. type: String,
  38. value: WHITE,
  39. },
  40. color: {
  41. type: [String, Object],
  42. value: BLUE,
  43. observer() {
  44. this.setHoverColor().then(() => {
  45. this.drawCircle(this.currentValue);
  46. });
  47. },
  48. },
  49. type: {
  50. type: String,
  51. value: '',
  52. },
  53. strokeWidth: {
  54. type: Number,
  55. value: 4,
  56. },
  57. clockwise: {
  58. type: Boolean,
  59. value: true,
  60. },
  61. },
  62. data: {
  63. hoverColor: BLUE,
  64. },
  65. methods: {
  66. getContext() {
  67. const { type, size } = this.data;
  68. if (type === '') {
  69. const ctx = wx.createCanvasContext('van-circle', this);
  70. return Promise.resolve(ctx);
  71. }
  72. const dpr = getSystemInfoSync().pixelRatio;
  73. return new Promise((resolve) => {
  74. wx.createSelectorQuery()
  75. .in(this)
  76. .select('#van-circle')
  77. .node()
  78. .exec((res) => {
  79. const canvas = res[0].node;
  80. const ctx = canvas.getContext(type);
  81. if (!this.inited) {
  82. this.inited = true;
  83. canvas.width = size * dpr;
  84. canvas.height = size * dpr;
  85. ctx.scale(dpr, dpr);
  86. }
  87. resolve(adaptor(ctx));
  88. });
  89. });
  90. },
  91. setHoverColor() {
  92. const { color, size } = this.data;
  93. if (isObj(color)) {
  94. return this.getContext().then((context) => {
  95. const LinearColor = context.createLinearGradient(size, 0, 0, 0);
  96. Object.keys(color)
  97. .sort((a, b) => parseFloat(a) - parseFloat(b))
  98. .map((key) =>
  99. LinearColor.addColorStop(parseFloat(key) / 100, color[key])
  100. );
  101. this.hoverColor = LinearColor;
  102. });
  103. }
  104. this.hoverColor = color;
  105. return Promise.resolve();
  106. },
  107. presetCanvas(context, strokeStyle, beginAngle, endAngle, fill) {
  108. const { strokeWidth, lineCap, clockwise, size } = this.data;
  109. const position = size / 2;
  110. const radius = position - strokeWidth / 2;
  111. context.setStrokeStyle(strokeStyle);
  112. context.setLineWidth(strokeWidth);
  113. context.setLineCap(lineCap);
  114. context.beginPath();
  115. context.arc(position, position, radius, beginAngle, endAngle, !clockwise);
  116. context.stroke();
  117. if (fill) {
  118. context.setFillStyle(fill);
  119. context.fill();
  120. }
  121. },
  122. renderLayerCircle(context) {
  123. const { layerColor, fill } = this.data;
  124. this.presetCanvas(context, layerColor, 0, PERIMETER, fill);
  125. },
  126. renderHoverCircle(context, formatValue) {
  127. const { clockwise } = this.data;
  128. // 结束角度
  129. const progress = PERIMETER * (formatValue / 100);
  130. const endAngle = clockwise
  131. ? BEGIN_ANGLE + progress
  132. : 3 * Math.PI - (BEGIN_ANGLE + progress);
  133. this.presetCanvas(context, this.hoverColor, BEGIN_ANGLE, endAngle);
  134. },
  135. drawCircle(currentValue) {
  136. const { size } = this.data;
  137. this.getContext().then((context) => {
  138. context.clearRect(0, 0, size, size);
  139. this.renderLayerCircle(context);
  140. const formatValue = format(currentValue);
  141. if (formatValue !== 0) {
  142. this.renderHoverCircle(context, formatValue);
  143. }
  144. context.draw();
  145. });
  146. },
  147. reRender() {
  148. // tofector 动画暂时没有想到好的解决方案
  149. const { value, speed } = this.data;
  150. if (speed <= 0 || speed > 1000) {
  151. this.drawCircle(value);
  152. return;
  153. }
  154. this.clearInterval();
  155. this.currentValue = this.currentValue || 0;
  156. this.interval = setInterval(() => {
  157. if (this.currentValue !== value) {
  158. if (this.currentValue < value) {
  159. this.currentValue += STEP;
  160. } else {
  161. this.currentValue -= STEP;
  162. }
  163. this.drawCircle(this.currentValue);
  164. } else {
  165. this.clearInterval();
  166. }
  167. }, 1000 / speed);
  168. },
  169. clearInterval() {
  170. if (this.interval) {
  171. clearInterval(this.interval);
  172. this.interval = null;
  173. }
  174. },
  175. },
  176. mounted() {
  177. this.currentValue = this.data.value;
  178. this.setHoverColor().then(() => {
  179. this.drawCircle(this.currentValue);
  180. });
  181. },
  182. destroyed() {
  183. this.clearInterval();
  184. },
  185. });