circle-progress-bar.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <view class="circle-progress-bar"
  3. :style="{
  4. width: sunit(size),
  5. height: sunit(size),
  6. }">
  7. <view class="circle" :change:prop="animateModule.pro" :prop="cpro"
  8. :data-animate="animate"
  9. :style="{
  10. transform: `rotate(${start * 360 + 45}deg)`,
  11. border: `${sunit(border_width)} solid ${border_color}`,
  12. }">
  13. </view>
  14. <view class="bg" v-if="background"
  15. :style="{
  16. background: background,
  17. }"></view>
  18. <view class="border-back" v-if="border_back_color"
  19. :style="{
  20. border: `${sunit(border_width)} solid ${border_back_color}`
  21. }"></view>
  22. <view class="center">
  23. <slot :pro="cpro"></slot>
  24. </view>
  25. </view>
  26. </template>
  27. <script module="animateModule" lang="wxs">
  28. var Timing = {
  29. easeIn: function easeIn(pos) {
  30. return Math.pow(pos, 3);
  31. },
  32. easeOut: function easeOut(pos) {
  33. return Math.pow(pos - 1, 3) + 1;
  34. },
  35. easeInOut: function easeInOut(pos) {
  36. if ((pos /= 0.5) < 1) {
  37. return 0.5 * Math.pow(pos, 3);
  38. } else {
  39. return 0.5 * (Math.pow(pos - 2, 3) + 2);
  40. }
  41. },
  42. linear: function linear(pos) {
  43. return pos;
  44. }
  45. };
  46. //#ifdef MP
  47. function setTimeout(t, cb, d) {
  48. if (d > 0) {
  49. var s = getDate().getTime();
  50. var fn = function () {
  51. if (getDate().getTime() - s > d) {
  52. cb && cb();
  53. } else
  54. t.requestAnimationFrame(fn);
  55. }
  56. fn();
  57. }
  58. else
  59. cb && cb();
  60. }
  61. //#endif
  62. function Animation(opts) {
  63. opts.duration = typeof opts.duration === 'undefined' ? 1000 : opts.duration;
  64. opts.timing = opts.timing || 'linear';
  65. var delay = 17;
  66. function createAnimationFrame() {
  67. if (typeof setTimeout !== 'undefined') {
  68. return function(step, delay) {
  69. //#ifndef MP
  70. setTimeout(function() {
  71. var timeStamp = +new Date();
  72. step(timeStamp);
  73. }, delay);
  74. //#endif
  75. //#ifdef MP
  76. setTimeout(opts.instance, function () {
  77. var timeStamp = getDate()
  78. step(timeStamp);
  79. }, delay)
  80. //#endif
  81. };
  82. } else if (typeof requestAnimationFrame !== 'undefined') {
  83. return requestAnimationFrame;
  84. } else {
  85. return function(step) {
  86. step(null);
  87. };
  88. }
  89. };
  90. var animationFrame = createAnimationFrame();
  91. var startTimeStamp = null;
  92. var _step = function step(timestamp) {
  93. if (timestamp === null) {
  94. opts.onProcess && opts.onProcess(1);
  95. opts.onAnimationFinish && opts.onAnimationFinish();
  96. return;
  97. }
  98. if (startTimeStamp === null) {
  99. startTimeStamp = timestamp;
  100. }
  101. if (timestamp - startTimeStamp < opts.duration) {
  102. var process = (timestamp - startTimeStamp) / opts.duration;
  103. var timingFunction = Timing[opts.timing];
  104. process = timingFunction(process);
  105. opts.onProcess && opts.onProcess(process);
  106. animationFrame(_step, delay);
  107. } else {
  108. opts.onProcess && opts.onProcess(1);
  109. opts.onAnimationFinish && opts.onAnimationFinish();
  110. }
  111. };
  112. animationFrame(_step, delay);
  113. }
  114. function getPath(deg) {
  115. var path = '50% 50%'
  116. //各个锚点
  117. var ps = ['0% 0%', '100% 0%', '100% 100%', '0% 100%']
  118. var ps1 = path + ',' + ps[0]
  119. var ps2 = ps1 + ',' + ps[1]
  120. var ps3 = ps2 + ',' + ps[2]
  121. var ps4 = ps3 + ',' + ps[3]
  122. var ops = [
  123. function(per) { return ps1 + ',' + (per + '% 0%') },
  124. function(per) { return ps2 + ',' + ('100% ' + per + '%') },
  125. function(per) { return ps3 + ',' + (100 - per) + '% 100%' },
  126. function(per) { return ps4 + ',' + '0% ' + (100 - per) + '%' },
  127. ]
  128. if (deg == 0) {
  129. return 'polygon(50% 50%, 50% 0%)'
  130. }
  131. else if (deg % 360 == 0) {
  132. return ''
  133. }
  134. var idx = parseInt(deg / 90) % 4
  135. var pdeg = deg % 90
  136. var per = pdeg / 90 * 100
  137. if(ops[idx]) {
  138. return 'polygon(' + ops[idx](per) + ')'
  139. }
  140. else {
  141. return ''
  142. }
  143. }
  144. function setDeg(newValue, oldValue, ownerInstance, instance) {
  145. var odeg = oldValue * 360
  146. var deg = newValue * 360
  147. var offset = deg - odeg
  148. var ds = instance.getDataset()
  149. if(!ds.animate) {
  150. var path = getPath(deg)
  151. instance.setStyle({
  152. 'clip-path': path,
  153. })
  154. return
  155. }
  156. Animation({
  157. instance: ownerInstance,
  158. timing: 'easeInOut',
  159. duration: 300,
  160. onProcess: function onProcess(process) {
  161. var pdeg = odeg + process * offset
  162. var path = getPath(pdeg)
  163. var com = ownerInstance.selectComponent('.circle');
  164. com.setStyle({
  165. 'clip-path': path,
  166. })
  167. },
  168. onAnimationFinish: function onAnimationFinish() {}
  169. });
  170. }
  171. module.exports = {
  172. pro: setDeg,
  173. }
  174. </script>
  175. <script>
  176. export default {
  177. props: {
  178. pro: {
  179. type: Number,
  180. default: 0
  181. },
  182. //起始位置 0-1
  183. start: {
  184. type: Number,
  185. default: 0,
  186. },
  187. //圆形大小
  188. size: {
  189. type: Number,
  190. default: 200
  191. },
  192. //线宽度
  193. border_width: {
  194. type: Number,
  195. default: 20
  196. },
  197. //线颜色
  198. border_color: {
  199. type: String,
  200. default: '#07C160',
  201. },
  202. //线背景色
  203. border_back_color: {
  204. type: String,
  205. },
  206. //中心内容背景色
  207. background: {
  208. type: String,
  209. },
  210. //单位
  211. unit: {
  212. type: String,
  213. default: 'px',
  214. },
  215. //是否启用动画
  216. animate:{
  217. type: Boolean,
  218. default: true,
  219. }
  220. },
  221. data() {
  222. return {
  223. cpro: 0,
  224. }
  225. },
  226. watch: {
  227. pro(val) {
  228. this.cpro = val
  229. }
  230. },
  231. mounted() {
  232. this.cpro = this.pro
  233. },
  234. methods: {
  235. sunit(num) {
  236. if(typeof num === 'number') {
  237. return num + this.unit
  238. }
  239. }
  240. }
  241. }
  242. </script>
  243. <style scoped lang="scss">
  244. .circle-progress-bar{
  245. position: relative;
  246. }
  247. .circle, .bg, .border-back {
  248. height: 100%;
  249. width: 100%;
  250. border-radius: 50%;
  251. position: absolute;
  252. box-sizing: border-box;
  253. }
  254. .circle{
  255. z-index: 1;
  256. }
  257. .border-back{
  258. height: 100%;
  259. width: 100%;
  260. left: 50%;
  261. top: 50%;
  262. transform: translate(-50%, -50%);
  263. }
  264. .point {
  265. position: absolute;
  266. border-radius: 50%;
  267. z-index: 1;
  268. }
  269. .center{
  270. position: absolute;
  271. left: 50%;
  272. top: 50%;
  273. transform: translate(-50%, -50%);
  274. z-index: 2;
  275. }
  276. </style>