tm-propressRound.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <template>
  2. <view class="tm-propressRound d-inline-block">
  3. <view class="tm-propressRound-text" :style="{
  4. width:size+'px',
  5. height:size+'px'
  6. }">
  7. <slot name="default" :value="value">
  8. <text class="text-black" :class="[black?'text-white':'']">{{value}}%</text>
  9. </slot>
  10. </view>
  11. <view class="tm-propressRound-rk">
  12. <!-- #ifdef MP-WEIXIN || MP-ALIPAY -->
  13. <canvas :canvas-id="cid" :id="cid"
  14. :style="{
  15. width:size+'px',
  16. height:size+'px'
  17. }"
  18. type="2d"
  19. ></canvas>
  20. <!-- #endif -->
  21. <!-- #ifndef MP-WEIXIN || MP-ALIPAY -->
  22. <canvas :canvas-id="cid" :id="cid"
  23. :style="{
  24. width:size+'px',
  25. height:size+'px'
  26. }"
  27. ></canvas>
  28. <!-- #endif -->
  29. </view>
  30. </view>
  31. </template>
  32. <script>
  33. /**
  34. * 环形进度条
  35. * @property {Number} value = [] 默认0,推荐使用v-model赋值。value.sync等同。
  36. * @property {Number} size = [] 默认100,圆环大小。
  37. * @property {Number} line-width = [] 默认6,圆环线条宽。
  38. * @property {Array} color = [] 默认['#FF9800','#E91E63','#FF5722'],激活颜色
  39. * @property {String} bgcolor = [] 默认'#EEEEEE',背景颜色
  40. * @property {Boolea} semicircle = [] 默认 false,是否半圆
  41. * @property {Function} input 变化时的值。
  42. * @property {Function} change 变化时的值。
  43. * @example <tm-propressRound v-model="checked"></tm-propressRound>
  44. */
  45. export default {
  46. name:"tm-propressRound",
  47. model:{
  48. prop:"value",
  49. event:"input"
  50. },
  51. props:{
  52. black:{
  53. type:Boolean,
  54. default:false
  55. },
  56. size:{
  57. type:Number,
  58. default:100
  59. },
  60. lineWidth:{
  61. type:Number,
  62. default:10
  63. },
  64. value:{
  65. type:Number,
  66. default:0
  67. },
  68. color:{
  69. type:Array,
  70. default:()=>{
  71. return ['rgb(255,152,0)','rgb(233,30,99)','rgb(255,87,34)']
  72. }
  73. },
  74. bgcolor:{
  75. type:String,
  76. default:'rgb(238,238,238)'
  77. },
  78. semicircle:{
  79. type:Boolean|String,
  80. default:false
  81. }
  82. },
  83. data() {
  84. return {
  85. ctx:null,
  86. timid:null,
  87. jishu:0,
  88. cid:'fdd_psd',
  89. is2d:false
  90. };
  91. },
  92. computed:{
  93. },
  94. created() {
  95. // #ifdef H5 || APP-VUE || APP-PLUS
  96. this.cid = uni.$tm.guid();
  97. // #endif
  98. // #ifdef MP-WEIXIN || MP-ALIPAY
  99. this.is2d=true;
  100. // #endif
  101. },
  102. mounted() {
  103. this.$nextTick(function(){
  104. this.inits();
  105. })
  106. },
  107. destroyed() {
  108. clearInterval(this.timid)
  109. },
  110. watch:{
  111. value:function(newval,oldval){
  112. if(newval < 0){
  113. this.$emit("input",0)
  114. this.$emit("update:value",0)
  115. this.$emit("change",0)
  116. return;
  117. }else if(newval >100){
  118. this.$emit("input",100)
  119. this.$emit("update:value",100)
  120. this.$emit("change",100)
  121. return;
  122. }
  123. this.$emit("input",newval)
  124. this.$emit("update:value",newval)
  125. this.$emit("change",newval)
  126. let blv = newval - oldval;
  127. if(newval>=100){
  128. blv = 100 - oldval;
  129. }
  130. if(blv >= 0 ){
  131. this.startHuiZhi(blv,oldval,true)
  132. }else{
  133. if(newval<=0){
  134. blv = oldval;
  135. }
  136. this.startHuiZhi(Math.abs(blv),oldval,false)
  137. }
  138. }
  139. },
  140. methods: {
  141. inits(){
  142. let t = this;
  143. if(this.is2d){
  144. const query = wx.createSelectorQuery().in(this)
  145. query.select('#'+this.cid)
  146. .node((res) => {
  147. const canvas = res.node
  148. const ctx = canvas.getContext('2d')
  149. const dpr = uni.getSystemInfoSync().pixelRatio
  150. canvas.width = res.node._width * dpr
  151. canvas.height = res.node._height * dpr
  152. ctx.scale(dpr, dpr)
  153. t.ctx = ctx;
  154. t.creatShape();
  155. this.startHuiZhi(this.value,0,true)
  156. })
  157. .exec()
  158. }else{
  159. this.ctx = uni.createCanvasContext(this.cid,this)
  160. this.creatShape();
  161. this.startHuiZhi(this.value,0,true)
  162. }
  163. },
  164. startHuiZhi(val,oldv,type){
  165. clearInterval(this.timid);
  166. let i = 0;
  167. let t = this;
  168. // #ifdef H5
  169. this.timid = setInterval(()=>{
  170. i += 1;
  171. if(i>=val){
  172. clearInterval(t.timid);
  173. return;
  174. }
  175. if(type){
  176. t.huizhired(oldv+i,type);
  177. }else{
  178. t.huizhired(oldv-i,type);
  179. }
  180. },5)
  181. // #endif
  182. // #ifndef H5
  183. if(type){
  184. t.huizhired(val+oldv,type);
  185. }else{
  186. t.huizhired(oldv-val,type);
  187. }
  188. // #endif
  189. },
  190. // 重新绘制。
  191. creatShape() {
  192. if(!this.ctx) return;
  193. let ctx= this.ctx;
  194. let x,y,r ;
  195. x = y = this.size / 2;
  196. r = (this.size - this.lineWidth*2) / 2
  197. if(this.is2d){
  198. ctx.beginPath()
  199. ctx.lineCap='round';
  200. ctx.lineWidth=this.lineWidth-1;
  201. ctx.strokeStyle=this.bgcolor;
  202. if(this.semicircle){
  203. ctx.arc(x, y, r, Math.PI, 0)
  204. }else{
  205. ctx.arc(x, y, r, -90, 2 * Math.PI)
  206. }
  207. ctx.stroke()
  208. }else{
  209. ctx.beginPath()
  210. ctx.setLineCap('round')
  211. ctx.setLineWidth(this.lineWidth-1)
  212. ctx.setStrokeStyle(this.bgcolor)
  213. if(this.semicircle){
  214. ctx.arc(x, y, r, Math.PI, 0)
  215. }else{
  216. ctx.arc(x, y, r, -90, 2 * Math.PI)
  217. }
  218. ctx.stroke()
  219. ctx.draw(true)
  220. }
  221. },
  222. huizhired(end=0,type){
  223. if(!this.ctx) return;
  224. let ctx = this.ctx;
  225. if(end>=100) end = 100;
  226. if(end<=0) end = 0;
  227. let bl = end * 3.6;
  228. bl = bl-90
  229. let x,y,r ;
  230. x = y = this.size / 2;
  231. r = (this.size - this.lineWidth*2) / 2
  232. if(this.is2d){
  233. ctx.beginPath()
  234. ctx.lineCap='round';
  235. ctx.lineWidth=type?this.lineWidth-1:this.lineWidth+1;
  236. ctx.strokeStyle=this.bgcolor
  237. if(this.semicircle){
  238. ctx.arc(x, y, r, Math.PI, 0)
  239. }else{
  240. ctx.arc(x, y, r, -90, 2 * Math.PI)
  241. }
  242. ctx.stroke()
  243. ctx.beginPath()
  244. ctx.lineCap='round';
  245. ctx.lineWidth=this.lineWidth
  246. var grd=ctx.createLinearGradient(x,0,0,2*x);
  247. let colorl = this.color.length;
  248. if(colorl==1){
  249. grd.addColorStop(1,this.color[0]);
  250. }else if(colorl>1){
  251. for(let i=0;i<colorl;i++){
  252. grd.addColorStop(i/(colorl-1),this.color[i]);
  253. }
  254. }
  255. ctx.strokeStyle=grd
  256. if(this.semicircle){
  257. let val = this.value;
  258. val = val <=0?0:val;
  259. val = val>=100?100:val;
  260. bl = -(Math.PI-this.value/100 * Math.PI)
  261. ctx.arc(x, y, r, Math.PI, bl)
  262. }else{
  263. ctx.arc(x, y, r, -90 * (Math.PI / 180), bl * (Math.PI / 180))
  264. }
  265. ctx.stroke()
  266. }else{
  267. ctx.beginPath()
  268. ctx.setLineCap('round')
  269. ctx.setLineWidth(type?this.lineWidth-1:this.lineWidth+1)
  270. ctx.setStrokeStyle(this.bgcolor)
  271. if(this.semicircle){
  272. ctx.arc(x, y, r, Math.PI, 0)
  273. }else{
  274. ctx.arc(x, y, r, -90, 2 * Math.PI)
  275. }
  276. ctx.stroke()
  277. ctx.draw(true)
  278. ctx.beginPath()
  279. ctx.setLineCap('round')
  280. ctx.setLineWidth(this.lineWidth)
  281. var grd=ctx.createLinearGradient(x,0,0,2*x);
  282. let colorl = this.color.length;
  283. if(colorl==1){
  284. grd.addColorStop(1,this.color[0]);
  285. }else if(colorl>1){
  286. for(let i=0;i<colorl;i++){
  287. grd.addColorStop(i/(colorl-1),this.color[i]);
  288. }
  289. }
  290. ctx.setStrokeStyle(grd)
  291. if(this.semicircle){
  292. let val = this.value;
  293. val = val <=0?Math.PI:val;
  294. val = val>=100?100:val;
  295. bl = -(Math.PI-this.value/100 * Math.PI)
  296. bl = Math.abs(bl)>=Math.PI?Math.PI:bl;
  297. ctx.arc(x, y, r, Math.PI, bl)
  298. }else{
  299. ctx.arc(x, y, r, -90 * (Math.PI / 180), bl * (Math.PI / 180))
  300. }
  301. ctx.stroke()
  302. ctx.draw(true)
  303. }
  304. },
  305. },
  306. }
  307. </script>
  308. <style lang="scss" scoped>
  309. .tm-propressRound{
  310. position: relative;
  311. .tm-propressRound-text{
  312. position: absolute;
  313. display: flex;
  314. justify-content: center;
  315. align-items: center;
  316. align-content: center;
  317. }
  318. }
  319. </style>