哈?標題不知道啥意思?css
老規矩,直接看圖!html
效果以下:vue
高清大圖!ajax
碼農多年,老眼昏花,動圖看不清?!那就看靜態截圖!!!canvas
不一樣分值效果以下:api
看完了賣家秀,咱們來看產品的製做過程吧!app
canvas#baseCanvas是底部的灰色圓環dom
canvas#myCanvas是上邊的彩色圓環ide
須要用css樣式幫助咱們把彩色圓環蓋到灰色圓環上邊。函數
這段代碼也很簡單,看canvas的api便可
3-一、vue組件中,script標籤頂部定義須要用的變量
3-二、vue的methos對象中,定義方法三個:
如下是三個方法的代碼:
上邊三個方法裏邊的代碼,幾乎都是對canvas API的應用,看教程便可。
只有draoClrCanvas方法中,canvas圓形的繪製時,arc的參數裏關於開始值、結束值的設置。
開始值決定了圓環的起始繪製位置,結束值決定告終束的位置(我好像說了一句廢話,可是左思右想後的思想描述文字,不想刪掉哈哈哈)
這個結束值的計算,對於我來講仍是比較麻煩的。
count變量爲何要這麼計算,我也忘了我是怎麼鼓搗出來的了。
this.grade是100之內的正整數,表示分值。被定義在data中,默認是0分。
因此一開始彩色圓環就看不見,由於起始點和結束點都是0點。
若是更改grade的值,從0-100,canvas彩色圓環的值也就會更改。
這樣,只要咱們逐漸修改grade的值,從新繪製,彩色圓環就會逐漸遞增,實現動畫效果。
因爲我這裏需求特殊,須要用戶每次翻到canvas所在swiper時,纔會觸發動畫(後來更麻煩一點須要柱狀圖和canvas部分有個入場效果後,動畫纔開始。效果就是上圖中最長的那張gif動畫那樣)。
因此我得藉助swiper才能實現。在swiper切換的回調函數中,從0開始不停遞增grade分數,並從新觸發彩色圓環的繪製,進而實現動畫效果。
vue中我用的swiper是'vue-awesome-swiper'。她的用法我在其餘文章中寫過步驟【點擊進入傳送門】。
swiper在vue-data中的配置裏,有一個on對象。在on對象中的slideChange函數,就是每次翻頁swiper時會觸發的回調函數。
這裏我說一下幾個比較特殊的點:
(1)vm:是我早就在vue的script中存儲的變量,初始化爲null,而後在mounted中,將其賦值爲vue實例對象。
初始化數據、繪製灰色圓環
經過這種方法,我在vue實例對象 - data - swiper - 回調函數中去拿vue實例對象 - data中的grade和gradeTarget屬性值,並對其進行修改。
ps:我也不知道這麼作是否是很傻的一種作法,當時作到這裏時是我遇到的一個難題,不知道怎麼在swiper的on回調中獲取vue實例。因而就有了這麼曲線救國的方法。若是看官有更好的解決方案,但願能夠給我提供一個新的思路,感激涕零哦親
(2) (this.activeIndex == 2 && vm.isStar) || (this.activeIndex == 1 && !vm.isStar)
這裏是由於業務,才這麼判斷,能夠忽略。
this在swiperChange函數中指向swiper對象。this.activeIndex是swiper實例的屬性,用官方的話說「返回當前活動塊(激活塊)的索引。」能夠理解他指的是當前翻到的是哪一頁,就是當前你所看的swiper-slide的下標。
我由於用戶的身份,會判斷性的決定當前canvas所在swiper前一頁是否展現。 若是不展現就根本不會繪製前一頁,那麼相應的當前頁的swiper的下標就會變成(index-1)。
總而言之,當知足條件、用戶翻到canvas所在swiper頁面後,我就要觸發if裏邊的圓環繪製邏輯。不然就走到else裏初始化數據頁面的狀態、清除定時器暫停動畫、並把彩色圓環清空
(3)vm.aniShow
在我上篇《純css繪製柱狀圖》裏邊說了,柱狀圖的動畫要跟canvas的動畫一塊兒說。由於他們的動畫實現須要配合swiper的切換。說的就是這裏的代碼:
vue - data - aniShow屬性變爲true時,div.row就會添加ani這個class類名:
一樣,aniShow爲true,progress的高度就會附上本身的目標值,也就是這個progress的實際高度通過百分制轉化後被賦予給了style屬性的height。(具體換算規則仍是見上篇《純css繪製柱狀圖》)
此時,由於progress的transition監聽了height變化,就開始有了高度漸增的柱狀圖遞增動畫了。
而ani類名下,progress的transition-delay實現了其高度錯開遞增效果。
可能只看文字描述很晦澀,再看一眼效果:
(4)彩色圓環繪製代碼部分
gradeTarget是實際分值,是最終要繪製到的結果。
grade從0開始,自增到gradeTarget的大小。
這裏我沒有直接++vm.grade,我也不知道本身當時咋想的。
if判斷,若是grade遞增到了目標值gradeTarget或者大於目標值,就中止遞增,並讓grade=gradeTarget。屬於臨界值的判斷。在運動功能中,又算碰撞檢測。
反之,不到目標的話,就清除上一次繪製的canvas畫布,在grade遞增變化後從新繪製新的彩色圓環。
(5)全部這些放到setTimeout中,暫停500毫秒再執行,是爲了等柱圖和環圖入場後,在開始繪製圓環的遞增效果。
其實上邊代碼都是很簡單的邏輯處理,看官們讀一遍代碼應該就差不離了。
新想法:
這個效果是我好久之前作的,今天在整理製做方法的時候,我想到本身代碼的一種優化方案:
其實不必在定時器裏從新調用彩色圓環繪製方法。咱們直接改的是this.grade屬性,監聽這個屬性的改變就行了其實。這樣此屬性在定時器中被修改,圓環方法就會自動執行。
這仍是一個想法,還須要個人實踐。
由於grade是每次遞增的分數,因此利用vue的雙向數據綁定,直接把grade看成分數值綁定到對應dom視圖處便可。
最後,圓環和上邊柱狀圖的動畫結合,就是animation控制一下動畫延遲便可。很簡單的。
(注,源碼稍做整理,單獨提取。爲了完整性也爲了保護其餘業務代碼,部分變量名作了修改,可能會和以前截圖中略微不一樣)
1 <template lang='pug'> 2 .indexs#Indexs.app-bg 3 transition(name="fade") 4 swiper#swiperBox(:options="swiperOption" ref="mySwiper") 5 swiper-slide.swiper-slide1 6 .container 7 .up 8 swiper-slide.swiper-slide2(v-if="isShow") 9 .my-shark 10 .up 11 swiper-slide.swiper-slide3 12 .container 13 .data-cont 14 .data.data01 15 .data01-charts 16 .row(v-for='item,index in Data' :key="index" :class='aniShow ? "ani":""') 17 .data-txt {{item.grade > 0 ? item.grade : '無數據'}} 18 .progress(:class='item.grade == 0 ? "nodata" : ""' :style="'height: ' + (aniShow ? (item.grade >= 100 ? (100 * 1.5) / 100 : item.grade == 0 ? 0.04 : item.grade * 1.5 / 100) : 0) +'rem'") 19 span.pg-data 20 .week {{item.week}} 21 .data.data02 22 .data02-charts 23 .canvas-box 24 //- baseCanvas 25 canvas#baseCanvas.my-canvas(ref="baseCanvas" width="174" height="174") 26 //- canvas 27 canvas#myCanvas.my-canvas.clr-canvas(ref="myCanvas" width="174" height="174") 28 .canvas-data #[span.num {{grade}}]分 29 30 </template> 31 <script> 32 var vm = null, 33 timer1 = null, 34 /* canvas基礎值 */ 35 c = null, //document.getElementById("myCanvas"); 36 ctx = null, //canvas-2d畫布 37 x = 161 / 2 + 1, //圓心座標 38 r = (161 - 10) / 2; //半徑大小 39 40 /* swiper組件 */ 41 import { swiper, swiperSlide } from "vue-awesome-swiper"; 42 import { getData } from "../io/getData"; 43 44 export default { 45 name: "Indexs", 46 components: { 47 swiper, 48 swiperSlide 49 }, 50 data() { 51 return { 52 grade: 0, //圓環圖分數 53 gradeTarget: 78.54, //實際得分數,可ajax請求數據後修改 54 isShow: true,//是否展現第二頁swiper 55 aniShow: false,//是否開啓柱圖動畫 56 Data:[{ 57 week: "第一週", 58 grade: 0 59 }, 60 { 61 week: "第二週", 62 grade: 30 63 }, 64 { 65 week: "第三週", 66 grade: 99.99 67 }, 68 { 69 week: "第四周", 70 grade: 76.98 71 }, 72 { 73 week: "第五週", 74 grade: 100 75 }], 76 77 swiperOption: { 78 //swiper參數 79 notNextTick: true, 80 direction: "vertical", 81 grabCursor: true, 82 setWrapperSize: true, 83 autoHeight: true, 84 slidesPerView: 1, 85 mousewheel: false, 86 mousewheelControl: false, 87 height: window.innerHeight, // 高度設置,佔滿設備高度 88 resistanceRatio: 0, 89 observeParents: true, 90 initialSlide: 2 - 1, //設置初始化時,swiper的默認展現頁面,從零開始 91 on: { 92 slideChange() { 93 if ( 94 (this.activeIndex == 2 && vm.isShow) || 95 (this.activeIndex == 1 && !vm.isShow) 96 ) { 97 console.log(this.activeIndex, vm.isShow, "繪製動畫"); 98 setTimeout(function() { 99 // 配合展現柱狀圖動畫 100 vm.aniShow = true; 101 // 定時器不斷觸發繪製彩色圓環,實現圓環動畫效果 102 timer1 = setInterval(function() { 103 // 中間分數文案更改 104 var num = vm.grade; 105 num++; 106 if (num >= vm.gradeTarget) { 107 vm.grade = vm.gradeTarget; 108 clearInterval(timer1); 109 } else { 110 vm.grade = num; 111 } 112 vm.clearCanvas(); 113 vm.drawClrCanvas(); 114 }, 1000 / 60); 115 }, 500); 116 } else { 117 // 翻頁後,初始化數據頁面的狀態、清除定時器暫停動畫、並把彩色圓環清空 118 console.log("其餘頁"); 119 clearInterval(timer1); 120 vm.grade = 0; 121 vm.aniShow = false; 122 vm.clearCanvas(); 123 } 124 } 125 } 126 } 127 }; 128 }, 129 computed: {}, 130 mounted() { 131 // 初始化數據、繪製灰色圓環 132 vm = this; 133 c = this.$refs.myCanvas; 134 ctx = c.getContext("2d"); 135 this.drawBaseCanvas(); 136 }, 137 methods: { 138 drawBaseCanvas() { 139 // canvas繪製 140 /* 基礎值 */ 141 var c = this.$refs.baseCanvas, //document.getElementById("myCanvas"); 142 // debugger; 143 ctx = c.getContext("2d"), 144 o = x, 145 randius = r; 146 /* 默認灰色圓圈 */ 147 ctx.strokeStyle = "#eee"; 148 ctx.lineWidth = 10; 149 ctx.beginPath(); 150 ctx.arc(o, o, randius, 0, 2 * Math.PI); 151 ctx.stroke(); 152 }, 153 clearCanvas() { 154 // 清除畫布 155 ctx.clearRect(0, 0, 200, 200); 156 }, 157 drawClrCanvas() { 158 var gradient = ctx.createLinearGradient(75, 50, 5, 90); 159 gradient.addColorStop("0", "#C88EFF"); 160 gradient.addColorStop("1.0", "#7E5CFF"); 161 ctx.strokeStyle = gradient; // 用漸變進行填充 162 ctx.lineWidth = 10; 163 ctx.lineCap = "round"; 164 ctx.shadowColor = "rgba(191,142,255, 0.36)"; 165 ctx.shadowBlur = 8; 166 ctx.shadowOffsetY = 8; 167 ctx.beginPath(); 168 var count = this.grade / (100 / 2) + 1; 169 ctx.arc(x, x, r, Math.PI, Math.PI * count, false); 170 ctx.stroke(); 171 } 172 } 173 }; 174 </script> 175 <style lang='scss'> 176 // 柱圖 177 .row { 178 position: relative; 179 z-index: 1; 180 width: 0.61rem; 181 margin-bottom: -0.28 - 0.08 - 0.38rem; 182 text-align: center; 183 } 184 185 .data-txt { 186 font-size: 0.2rem; 187 line-height: 0.2rem; 188 margin-bottom: 0.09rem; 189 } 190 191 .progress { 192 height: 0rem; 193 transition: height 0.5s ease-in-out; 194 } 195 196 .ani { 197 @for $i from 1 to 6 { 198 &:nth-of-type(#{$i}) { 199 .progress { 200 transition-delay: #{$i * 0.15}s; 201 } 202 } 203 } 204 // &:nth-of-type(1) { 205 // .progress { 206 // transition-delay: .4s; 207 // } 208 // } 209 210 // &:nth-of-type(2) { 211 // .progress { 212 // transition-delay: .8s; 213 // } 214 // } 215 216 // &:nth-of-type(3) { 217 // .progress { 218 // transition-delay: 1s; 219 // } 220 // } 221 222 // &:nth-of-type(4) { 223 // .progress { 224 // transition-delay: 1.4s; 225 // } 226 // } 227 228 // &:nth-of-type(5) { 229 // .progress { 230 // transition-delay: 1.8s; 231 // } 232 // } 233 } 234 235 .pg-data { 236 display: block; 237 width: 0.12rem; 238 height: 100%; 239 margin: 0 auto; 240 background: linear-gradient(0deg, #c88eff 0%, #7e5cff 100%); 241 box-shadow: 0 -0.04rem 0.14rem 0 rgba(129, 93, 255, 0.4); 242 border-radius: 0.05rem 0.05rem 0 0; 243 } 244 245 // 0分展現規則 246 .nodata { 247 .pg-data { 248 border-radius: 0; 249 background: #e7e7e7; 250 box-shadow: none; 251 } 252 } 253 254 .week { 255 font-size: 0.2rem; 256 line-height: 0.2rem; 257 margin-top: 0.08rem; 258 color: #666; 259 } 260 // 環圖 - data02數據部分 261 .data02-charts { 262 margin-top: 0.32rem; 263 height: 1.61rem; 264 } 265 266 .canvas-box { 267 position: relative; 268 float: left; 269 width: 1.61rem; 270 height: 1.61rem; 271 margin-left: 0.92rem; 272 } 273 274 .my-canvas { 275 width: 1.61rem; 276 height: 1.61rem; 277 } 278 .clr-canvas { 279 position: absolute; 280 top: 0; 281 left: 0; 282 } 283 284 .canvas-data { 285 position: absolute; 286 top: 0.56rem; 287 left: 0; 288 right: 0; 289 margin: auto; 290 margin-left: -0.1rem; 291 text-align: center; 292 font-size: 0.24rem; 293 294 .num { 295 font-size: 0.32rem; 296 font-weight: 600; 297 } 298 } 299 </style>