index.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import { use } from '../utils';
  2. import { raf, cancel } from '../utils/raf';
  3. import { BLUE, WHITE } from '../utils/color';
  4. const [sfc, bem] = use('circle');
  5. const PERIMETER = 3140;
  6. const PATH = 'M 530 530 m -500, 0 a 500, 500 0 1, 1 1000, 0 a 500, 500 0 1, 1 -1000, 0';
  7. function format(rate) {
  8. return Math.min(Math.max(rate, 0), 100);
  9. }
  10. export default sfc({
  11. props: {
  12. text: String,
  13. value: Number,
  14. speed: Number,
  15. size: {
  16. type: String,
  17. default: '100px'
  18. },
  19. fill: {
  20. type: String,
  21. default: 'none'
  22. },
  23. rate: {
  24. type: Number,
  25. default: 100
  26. },
  27. layerColor: {
  28. type: String,
  29. default: WHITE
  30. },
  31. color: {
  32. type: String,
  33. default: BLUE
  34. },
  35. strokeWidth: {
  36. type: Number,
  37. default: 40
  38. },
  39. clockwise: {
  40. type: Boolean,
  41. default: true
  42. }
  43. },
  44. computed: {
  45. style() {
  46. return {
  47. width: this.size,
  48. height: this.size
  49. };
  50. },
  51. layerStyle() {
  52. let offset = (PERIMETER * (100 - this.value)) / 100;
  53. offset = this.clockwise ? offset : PERIMETER * 2 - offset;
  54. return {
  55. stroke: `${this.color}`,
  56. strokeDashoffset: `${offset}px`,
  57. strokeWidth: `${this.strokeWidth + 1}px`
  58. };
  59. },
  60. hoverStyle() {
  61. return {
  62. fill: `${this.fill}`,
  63. stroke: `${this.layerColor}`,
  64. strokeWidth: `${this.strokeWidth}px`
  65. };
  66. }
  67. },
  68. watch: {
  69. rate: {
  70. handler() {
  71. this.startTime = Date.now();
  72. this.startRate = this.value;
  73. this.endRate = format(this.rate);
  74. this.increase = this.endRate > this.startRate;
  75. this.duration = Math.abs(((this.startRate - this.endRate) * 1000) / this.speed);
  76. if (this.speed) {
  77. cancel(this.rafId);
  78. this.rafId = raf(this.animate);
  79. } else {
  80. this.$emit('input', this.endRate);
  81. }
  82. },
  83. immediate: true
  84. }
  85. },
  86. methods: {
  87. animate() {
  88. const now = Date.now();
  89. const progress = Math.min((now - this.startTime) / this.duration, 1);
  90. const rate = progress * (this.endRate - this.startRate) + this.startRate;
  91. this.$emit('input', format(parseFloat(rate.toFixed(1))));
  92. if (this.increase ? rate < this.endRate : rate > this.endRate) {
  93. this.rafId = raf(this.animate);
  94. }
  95. }
  96. },
  97. render(h) {
  98. return (
  99. <div class={bem()} style={this.style}>
  100. <svg viewBox="0 0 1060 1060">
  101. <path class={bem('hover')} style={this.hoverStyle} d={PATH} />
  102. <path class={bem('layer')} style={this.layerStyle} d={PATH} />
  103. </svg>
  104. {this.slots() || (this.text && <div class={bem('text')}>{this.text}</div>)}
  105. </div>
  106. );
  107. }
  108. });