As always, just look at the pictures!
The effect is as follows:
Big HD pictures!
I have been working as a coder for many years, but my old eyes are dim and I can’t see the animation clearly? ! Then look at the static screenshots! ! !
The effects of different scores are as follows:
After watching the seller’s show, let’s take a look at the production process of the product!
Canvas draws a circle 1. In vue, the code in <template lang=pug> is as follows:
canvas#baseCanvas is the gray ring at the bottom
canvas#myCanvas is the colored ring above
We need to use css style to help us cover the colored ring on top of the gray ring.
2. css style: 3. js-canvas style drawing codeThis code is also very simple, just look at the canvas api
3-1. In the vue component, define the required variables at the top of the script tag.
3-2. In the method object of vue, three methods are defined:
drawBaseCanvas: used to draw the bottom gray ring. Since the gray circle has no animation effect, just draw a complete gray circle at the beginning. drawClrCanvas: used to draw the colored ring above. clearCanvas: used to clear the canvas. This is what the colored ring animation requires. Because the core of our ring animation effect is to clear the colored ring every once in a while, then increase the end angle value and redraw it, so that it is continuous animation.
Here is the code for the three methods:
The codes in the above three methods are almost all applications of the canvas API, just read the tutorial.
Only in the draoClrCanvas method, when drawing a canvas circle, the start value and end value are set in the arc parameter.
The start value determines the starting drawing position of the circle, and the end value determines the ending position (I seem to have said nonsense, but after thinking hard about the thought description text, I don’t want to delete it hahaha)
The calculation of this end value is quite troublesome for me.
Why does the count variable need to be calculated like this? I forgot how I figured it out.
this.grade is a positive integer within 100, indicating the score. It is defined in data, and the default is 0 points.
So the colored circle is invisible at the beginning because the starting point and the ending point are both 0 points.
If you change the value of grade, from 0-100, the value of the canvas color ring will also change.
In this way, as long as we gradually modify the value of grade and redraw it, the colored rings will gradually increase to achieve the animation effect.
Ring animation effectDue to my special needs here, the animation needs to be triggered every time the user turns to the swiper where the canvas is located (later, it is more troublesome and requires the histogram and canvas parts to have an entrance effect before the animation starts. The effect is the longest in the picture above (like that gif animation).
So I have to use swiper to achieve it. In the callback function of the swiper switch, the grade score is continuously incremented from 0, and the drawing of the colored ring is retriggered to achieve the animation effect.
The swiper I use in vue is 'vue-awesome-swiper'. I have written the steps for her usage in other articles.
There is an on object in the configuration of swiper in vue-data. The slideChange function in the on object is the callback function that is triggered every time the swiper turns the page.
Here I will talk about a few special points:
(1) vm: It is a variable that I have stored in the vue script. It is initialized to null, and then in mounted, it is assigned as a vue instance object.
Initialize data and draw gray circles
Through this method, I get the grade and gradeTarget attribute values in the vue instance object-data-swiper-callback function and modify them.
ps: I don’t know if this is a stupid approach. When I did this, I encountered a problem. I didn’t know how to get the vue instance in the on callback of swiper. So there was such a curved way to save the country. If I have a better solution, I hope you can provide me with a new idea. Thank you very much, dear.
(2) (this.activeIndex == 2 && vm.isStar) || (this.activeIndex == 1 && !vm.isStar)
This judgment is made because of business and can be ignored.
this points to the swiper object in the swiperChange function. this.activeIndex is a property of the swiper instance. In official terms, it returns the index of the current active block (activation block). You can understand that he is referring to which page you are currently turning to, which is the subscript of the swiper-slide you are currently looking at.
Because of my identity as a user, I will decisively decide whether to display the previous page of the swiper where the current canvas is located. If it is not displayed, the previous page will not be drawn at all, then the corresponding swiper subscript of the current page will become (index-1).
All in all, when the conditions are met and the user turns to the swiper page where the canvas is located, I will trigger the ring drawing logic in the if. Otherwise, go to else to initialize the data page state, clear the timer to pause the animation, and clear the colored ring.
(3)vm.aniShow
As mentioned in my last article "Drawing Histograms with Pure CSS", the animation of the histogram should be discussed together with the animation of the canvas. Because their animation implementation needs to cooperate with swiper switching. That's the code here:
When the vue - data - aniShow attribute becomes true, the class name ani will be added to div.row:
Similarly, if aniShow is true, the height of the progress will be attached with its own target value, that is, the actual height of the progress will be assigned to the height of the style attribute after being converted in percentage.
At this time, because the progress transition monitors the height change, the histogram increasing animation with increasing height begins.
Under the name of the ani class, the transition-delay of progress achieves its highly staggered increment effect.
It may be confusing just to read the text description, but take another look at the effect:
(4) Color circle drawing code part
gradeTarget is the actual score, which is the final result to be drawn.
Grade starts from 0 and increases to the size of gradeTarget.
I didn't directly ++vm.grade here, and I don't know what I was thinking at the time.
If judge, if grade increments to the target value gradeTarget or is greater than the target value, stop incrementing and let grade=gradeTarget. Judgment that belongs to critical value. In the motion function, it is also considered collision detection.
On the contrary, if the target is not reached, the last drawn canvas will be cleared, and a new colored ring will be redrawn after the grade is incrementally changed.
(5) Put all these in setTimeout, pause for 500 milliseconds before executing, in order to wait for the bar chart and ring chart to enter the market before starting to draw the circle's incremental effect.
In fact, the above code is a very simple logical process. Readers should be able to understand it after reading the code once.
New ideas:
I made this effect a long time ago. When I was sorting out the production method today, I thought of an optimization solution for my own code:
In fact, there is no need to re-call the color circle drawing method in the timer. What we directly changed is the this.grade attribute. It would be good to monitor the changes of this attribute. In this way, when this property is modified in the timer, the ring method will be automatically executed.
This is still an idea and still needs my practice.
Incremental effect of middle text:Because grade is an incrementing score each time, you can use Vue's two-way data binding to directly bind grade as a score value to the corresponding DOM view.
Finally, the animation of the ring and the bar chart above is combined with animation to control the animation delay. Very simple.
index.vue source code:
(Note, the source code has been slightly organized and extracted separately. For completeness and to protect other business codes, some variable names have been modified, which may be slightly different from the previous screenshots)
<template lang='pug'> .indexs#Indexs.app-bg transition(name=fade) swiper#swiperBox(:options=swiperOption ref=mySwiper) swiper-slide.swiper-slide1 .container .up swiper-slide.swiper -slide2(v-if=isShow) .my-shark .up swiper-slide.swiper-slide3 .container .data-cont .data.data01 .data01-charts .row(v-for='item,index in Data' :key=index :class='aniShow ? ani:') . data-txt {{item.grade > 0 ? item.grade : 'No data'}} .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') span.pg-data .week {{item.week}} .data.data02 .data02-charts .canvas-box //- baseCanvas canvas#baseCanvas.my-canvas(ref=baseCanvas width=174 height=174) //- canvas canvas#myCanvas.my-canvas.clr-canvas(ref=myCanvas width =174 height=174) .canvas-data #[span.num {{grade}}]points</template><script>var vm = null, timer1 = null, /* canvas basic value*/ c = null, //document.getElementById(myCanvas); ctx = null, //canvas-2d canvas x = 161 / 2 + 1, //circle center coordinate r = (161 - 10) / 2; //Radius size/* swiper component*/import { swiper, swiperSlide } from vue-awesome-swiper;import { getData } from ../io/getData;export default { name: Indexs, components: { swiper, swiperSlide }, data() { return { grade: 0, //Donut chart score gradeTarget: 78.54, //Actual score number, you can modify isShow: true after requesting data via ajax, // Whether to display the second page swiper aniShow: false, // Whether to enable column chart animation Data: [{ week: first week, grade: 0 }, { week: second week, grade: 30 }, { week: third week, grade: 99.99 }, { week: fourth week, grade: 76.98 }, { week: fifth week, grade: 100 }], swiperOption: { //swiper parameters notNextTick: true, direction: vertical, grabCursor: true, setWrapperSize: true, autoHeight: true, slidesPerView: 1, mousewheel: false, mousewheelControl: false, height: window.innerHeight, //Height setting, filling the device height resistanceRatio: 0, observeParents: true, initialSlide: 2 - 1, //When setting initialization , the default display page of swiper, starting from scratch on: { slideChange() { if ( (this.activeIndex == 2 && vm.isShow) || (this.activeIndex == 1 && !vm.isShow) ) { console.log(this.activeIndex, vm.isShow, draw animation); setTimeout(function() { // Display histogram animation vm.aniShow = true; // The timer continuously triggers the drawing of colored rings to achieve the ring animation effect timer1 = setInterval(function() { // Change the copywriting of the intermediate score var num = vm.grade; num++; if (num >= vm.gradeTarget) { vm.grade = vm.gradeTarget; clearInterval(timer1); } else { vm.grade = num; } vm.clearCanvas(); vm.drawClrCanvas(); }, 1000 / 60); }, 500); } else { // After turning the page, initialize the status of the data page, clear the timer to pause the animation, and clear the colored rings console.log (other pages); clearInterval(timer1); vm.grade = 0; vm.aniShow = false; vm.clearCanvas (); } } } } }; }, computed: {}, mounted() { // Initialize data, draw gray circle vm = this; c = this.$refs.myCanvas; ctx = c.getContext(2d); this.drawBaseCanvas(); }, methods: { drawBaseCanvas() { // canvas drawing/* basic value*/ var c = this.$refs.baseCanvas , //document.getElementById(myCanvas); // debugger; ctx = c.getContext(2d), o = x, randius = r; /*Default gray circle*/ ctx.strokeStyle = #eee; ctx.lineWidth = 10; ctx.beginPath(); ctx.arc(o, o, randius, 0, 2 * Math.PI); ctx.stroke() ; }, clearCanvas() { // Clear the canvas ctx.clearRect(0, 0, 200, 200); }, drawClrCanvas() { var gradient = ctx.createLinearGradient(75, 50, 5, 90); gradient.addColorStop(0, #C88EFF); gradient.addColorStop(1.0, #7E5CFF); ctx.strokeStyle = gradient; // Use gradient To fill ctx.lineWidth = 10; ctx.lineCap = round; ctx.shadowColor = rgba(191,142,255, 0.36); ctx.shadowBlur = 8; ctx.shadowOffsetY = 8; ctx.beginPath(); var count = this.grade / (100 / 2) + 1; ctx.arc(x, x, r, Math.PI, Math.PI * count, false); ctx.stroke(); } }};</script><style lang='scss'>// column chart.row { position: relative; z-index: 1; width: 0.61rem; margin-bottom: -0.28 - 0.08 - 0.38rem; text-align: center;}.data-txt { font-size: 0.2rem; line-height: 0.2rem; margin-bottom: 0.09rem;}.progress { height: 0rem; transition: height 0.5s ease-in-out;}.ani { @for $i from 1 to 6 { &:nth-of-type( #{$i}) { .progress { transition-delay: #{$i * 0.15}s; } } } // &:nth-of-type(1) { // .progress { // transition-delay: .4s; // } // } // &:nth-of-type(2) { // .progress { // transition-delay: .8s; // } // } // &:nth-of-type(3) { // .progress { // transition-delay: 1s; // } // } // &:nth-of-type(4) { / / .progress { // transition-delay: 1.4s; // } // } // &:nth-of-type(5) { // .progress { // transition-delay: 1.8s; // } // }}.pg- data { display: block; width: 0.12rem; height: 100%; margin: 0 auto; background: linear-gradient(0deg, #c88eff 0%, #7e5cff 100%); box-shadow: 0 -0.04rem 0.14rem 0 rgba(129, 93, 255, 0.4); border-radius: 0.05rem 0.05rem 0 0;}//0 points display rule.nodata { .pg- data { border-radius: 0; background: #e7e7e7; box-shadow: none; }}.week { font-size: 0.2rem; line-height: 0.2rem; margin-top: 0.08rem; color: #666;}// Ring chart - data02 data part.data02-charts { margin-top : 0.32rem; height: 1.61rem;}.canvas-box { position: relative; float: left; width: 1.61rem; height: 1.61rem; margin-left: 0.92rem;}.my-canvas { width: 1.61rem; height: 1.61rem;}.clr-canvas { position: absolute; top: 0; left: 0;} .canvas-data { position: absolute; top: 0.56rem; left: 0; right: 0; margin: auto; margin-left: -0.1rem; text-align: center; font-size: 0.24rem; .num { font-size: 0.32rem; font-weight: 600; }}</style>
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.