index.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <template>
  2. <uni-shadow-root class="vant-circle-index"><view class="van-circle">
  3. <canvas class="van-circle__canvas" :style="style" canvas-id="van-circle"></canvas>
  4. <view v-if="(!text)" class="van-circle__text">
  5. <slot></slot>
  6. </view>
  7. <cover-view v-else class="van-circle__text">{{ text }}</cover-view>
  8. </view></uni-shadow-root>
  9. </template>
  10. <script>
  11. global['__wxRoute'] = 'vant/circle/index'
  12. import { VantComponent } from '../common/component';
  13. import { isObj } from '../common/utils';
  14. import { BLUE, WHITE } from '../common/color';
  15. function format(rate) {
  16. return Math.min(Math.max(rate, 0), 100);
  17. }
  18. const PERIMETER = 2 * Math.PI;
  19. const BEGIN_ANGLE = -Math.PI / 2;
  20. const STEP = 1;
  21. VantComponent({
  22. props: {
  23. text: String,
  24. lineCap: {
  25. type: String,
  26. value: 'round'
  27. },
  28. value: {
  29. type: Number,
  30. value: 0,
  31. observer: 'reRender'
  32. },
  33. speed: {
  34. type: Number,
  35. value: 50
  36. },
  37. size: {
  38. type: Number,
  39. value: 100,
  40. observer: 'setStyle'
  41. },
  42. fill: String,
  43. layerColor: {
  44. type: String,
  45. value: WHITE
  46. },
  47. color: {
  48. type: [String, Object],
  49. value: BLUE,
  50. observer: 'setHoverColor'
  51. },
  52. strokeWidth: {
  53. type: Number,
  54. value: 4
  55. },
  56. clockwise: {
  57. type: Boolean,
  58. value: true
  59. }
  60. },
  61. data: {
  62. style: 'width: 100px; height: 100px;',
  63. hoverColor: BLUE
  64. },
  65. methods: {
  66. getContext() {
  67. if (!this.ctx) {
  68. this.ctx = wx.createCanvasContext('van-circle', this);
  69. }
  70. return this.ctx;
  71. },
  72. setHoverColor() {
  73. const context = this.getContext();
  74. const { color, size } = this.data;
  75. let hoverColor = color;
  76. if (isObj(color)) {
  77. const LinearColor = context.createLinearGradient(size, 0, 0, 0);
  78. Object.keys(color)
  79. .sort((a, b) => parseFloat(a) - parseFloat(b))
  80. .map(key => LinearColor.addColorStop(parseFloat(key) / 100, color[key]));
  81. hoverColor = LinearColor;
  82. }
  83. this.setData({ hoverColor });
  84. },
  85. setStyle() {
  86. const { size } = this.data;
  87. const style = `width: ${size}px; height: ${size}px;`;
  88. this.setData({ style });
  89. },
  90. presetCanvas(context, strokeStyle, beginAngle, endAngle, fill) {
  91. const { strokeWidth, lineCap, clockwise, size } = this.data;
  92. const position = size / 2;
  93. const radius = position - strokeWidth / 2;
  94. context.setStrokeStyle(strokeStyle);
  95. context.setLineWidth(strokeWidth);
  96. context.setLineCap(lineCap);
  97. context.beginPath();
  98. context.arc(position, position, radius, beginAngle, endAngle, !clockwise);
  99. context.stroke();
  100. if (fill) {
  101. context.setFillStyle(fill);
  102. context.fill();
  103. }
  104. },
  105. renderLayerCircle(context) {
  106. const { layerColor, fill } = this.data;
  107. this.presetCanvas(context, layerColor, 0, PERIMETER, fill);
  108. },
  109. renderHoverCircle(context, formatValue) {
  110. const { clockwise, hoverColor } = this.data;
  111. // 结束角度
  112. const progress = PERIMETER * (formatValue / 100);
  113. const endAngle = clockwise
  114. ? BEGIN_ANGLE + progress
  115. : 3 * Math.PI - (BEGIN_ANGLE + progress);
  116. this.presetCanvas(context, hoverColor, BEGIN_ANGLE, endAngle);
  117. },
  118. drawCircle(currentValue) {
  119. const context = this.getContext();
  120. const { size } = this.data;
  121. context.clearRect(0, 0, size, size);
  122. this.renderLayerCircle(context);
  123. const formatValue = format(currentValue);
  124. if (formatValue !== 0) {
  125. this.renderHoverCircle(context, formatValue);
  126. }
  127. context.draw();
  128. },
  129. reRender() {
  130. // tofector 动画暂时没有想到好的解决方案
  131. const { value, speed } = this.data;
  132. if (speed <= 0 || speed > 1000) {
  133. this.drawCircle(value);
  134. return;
  135. }
  136. this.clearInterval();
  137. this.currentValue = this.currentValue || 0;
  138. this.interval = setInterval(() => {
  139. if (this.currentValue !== value) {
  140. if (this.currentValue < value) {
  141. this.currentValue += STEP;
  142. }
  143. else {
  144. this.currentValue -= STEP;
  145. }
  146. this.drawCircle(this.currentValue);
  147. }
  148. else {
  149. this.clearInterval();
  150. }
  151. }, 1000 / speed);
  152. },
  153. clearInterval() {
  154. if (this.interval) {
  155. clearInterval(this.interval);
  156. this.interval = null;
  157. }
  158. }
  159. },
  160. created() {
  161. const { value } = this.data;
  162. this.currentValue = value;
  163. this.drawCircle(value);
  164. },
  165. destroyed() {
  166. this.ctx = null;
  167. this.clearInterval();
  168. }
  169. });
  170. export default global['__wxComponents']['vant/circle/index']
  171. </script>
  172. <style platform="mp-weixin">
  173. @import '../common/index.css';.van-circle{position:relative;display:inline-block;text-align:center}.van-circle__text{position:absolute;top:50%;left:0;width:100%;-webkit-transform:translateY(-50%);transform:translateY(-50%);color:#323233;color:var(--circle-text-color,#323233)}
  174. </style>