Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money
preface
Blessed by Buddha, there is no bug. Hello everyone! I am across the sea!
Sometimes in a project we have to take a prototype from the UI and turn it into a concrete page that uses components that are not readily available. At this point, you need to implement these specific components yourself.
These are components that you can use, that are generic to you, that you can reuse.
Earlier, when we were still using H5, we realized this function once. Now our company’s technology stack has been transferred to Vue. Recently, a project needs this function, so we need to convert the previous code into Vue again
Introduction to implementation process
The effect is as follows:
Don't copy the code just yet, I'll show you the whole thing at the end
Let’s start with the implementation
The effect to be done is: after the dot popbox appears, an animated dashboard is displayed.
Break down the implementation of this feature:
- When a value changes to a new value, it is a gradual process;
- There is an elastic animation effect as the dashboard values change
- There is a vertical line at the end of the arc, as a pointer to the dashboard, when the dashboard value changes, there is an elastic animation effect.
Because I have checked the data before, D3 was used for H5 earlier, so I will continue to use D3 this time
D3 portal
Ps: MY D3 version at the time was D3_V5
Draw the dashboard
- So let’s define a
svg
Chemical element:
<svg id="myGauge" width="150" height="150" ></svg>
Copy the code
- Then, declare some variables for initialization:
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = defined parameters need to use the dashboard = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// The height and width of SVG can also be obtained from the width and height attributes of SVG
let width = 158;
let height = 158;
// Inner and outer radius of the arc
let innerRadius = 35;
let outerRadius = 60;
// Start and end angles of the arc
let arcMin = -Math.PI * 2 / 3;
let arcMax = Math.PI * 2 / 3;
let valueLabel = 0; // The value of AQI displayed on the dashboard
let foreground; // Represents the length of an AQI arc, which is another arc object. The undefined value defaults to undefined
let tick; // Indicates the pointer to the end of the QI arc. Undefined value defaults to undefined
let arc; // Represents the arc drawing function, undefined value default undefined
let background; // Set the color of the background arc. Undefined values default to undefined
let valueLevel; // Sets the severity level of AQI arcs. Undefined values default to undefined
let percentage; // Indicates the arc length corresponding to the AQI value, which is between 0 and 1. Undefined value defaults to undefined
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = defined parameters need to use the dashboard = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copy the code
- When it comes to the Angle of rotation, prepare an Angle of radians
angleToDegree
// The Angle turns radians
function angleToDegree(angle) {
return angle * 180 / Math.PI;
}
Copy the code
- D3 property tween (gradient) animation method that causes an arc to gradient from the current Angle to a new Angle
// Define a tween (gradient) animation method for the d property to gradient an arc from the current Angle to a new Angle.
function arcTween(newAngle) {
// let self = this;
return (d) = > {
const interpolate = d3.interpolate(d.endAngle, newAngle); // Find an interpolation between the two values
return (t) = > {
d.endAngle = interpolate(t); // Calculate interpolation based on transition time t and assign it to endAngle
// Returns the new "d" attribute value
return arc(d);
};
};
}
Copy the code
- My dashboard will display the value of AQI, different AQI values show different colors, define a style function
// Set the AQI dashboard style in the popbox according to the AQI value
/** * isAQI_arc: true to return the value of AQI from 1 to 500, false to return the value of AQI from 1, 2, 3, 4, 5
function AQIStyle(isAQI_arc, value) {
let level = 0;
let AQI_arc = '# 717171';
const valueNum = Number(value);
switch (true) {
case valueNum > 0 && valueNum <= 50:
if (isAQI_arc) {
AQI_arc = '#01e401';
} else {
level = 1;
}
break;
case valueNum > 50 && valueNum <= 100:
if (isAQI_arc) {
AQI_arc = '#ffff00';
} else {
level = 2;
}
break;
case valueNum > 100 && valueNum <= 150:
if (isAQI_arc) {
AQI_arc = '#ff7e00';
} else {
level = 3;
}
break;
case valueNum > 150 && valueNum <= 200:
if (isAQI_arc) {
AQI_arc = '#ff0000';
} else {
level = 4;
}
break;
case valueNum > 200 && valueNum <= 300:
if (isAQI_arc) {
AQI_arc = '#99004c';
} else {
level = 5;
}
break;
case valueNum > 300:
if (isAQI_arc) {
AQI_arc = '#7e0023';
} else {
level = 6;
}
break;
default:
if (isAQI_arc) {
AQI_arc = '# 717171';
} else {
level = 0;
}
break;
}
if (isAQI_arc) {
return AQI_arc;
} else {
returnlevel; }}Copy the code
- And then there’s
D3 API operation
, as well asOperations in SVG
The API used in D3 and SVG is introduced briefly
- D3 operates the portal
// Construct a new arc generator using the default Settings: create an instance of an arc
d3.arc()
const arc = d3.arc(); // Create an arc instance and set some properties
arc({
innerRadius: 0.// Set the radius of the arc
outerRadius: 100.// Set the radius of the arc
startAngle: 0.// Set the beginning of the arc
endAngle: Math.PI / 2 // Set the arc bottom cutoff radian
});
Copy the code
// Find the DOM element you need to manipulate to get SVG
d3.select('#id')
Copy the code
- Perform operations on SVG
// Get the SVG elements and convert the origin to the center of the canvas so that we do not need to specify their positions separately when we create arcs later
let svg = d3.select("#myGauge")
let g = svg.append("g").attr("transform"."translate(" + width / 2 + "," + height / 2 + ")");
Copy the code
- Add text (title, value, unit) to dashboard
// Add the dashboard title
g.append("text").attr("class"."gauge-title")
.style("alignment-baseline"."central") // Align relative to the parent element
.style("text-anchor"."middle") // Text anchor point, centered
.attr("y", -45) // The distance to the center
.text("CPU Usage");
// Add the value displayed on the dashboard, declare a variable because it will be updated later
var valueLabel = g.append("text").attr("class"."gauge-value")
.style("alignment-baseline"."central") // Align relative to the parent element
.style("text-anchor"."middle") // Text anchor point, centered
.attr("y".25) // The distance to the center
.text(12.65);
// Add a unit of value to the dashboard display
g.append("text").attr("class"."gauge-unity")
.style("alignment-baseline"."central") // Align relative to the parent element
.style("text-anchor"."middle") // Text anchor point, centered
.attr("y".40) // The distance to the center
.text("%");
Copy the code
D3
To make theSVG
Figure, andEcharts
Draw theCanvas
A very important advantage is that it can be usedCSS
defineSVG
Style. For example, the dashboard title here looks like this:
.gauge-title{
font-size: 10px;
fill: #A1A6AD;
}
Copy the code
- Add background arc
// Add background arc
var background = g.append("path")
.datum({endAngle:arcMax}) // Pass the endAngle argument to the arc method
.style("fill"."# 444851")
.attr("d", arc);
Copy the code
- Add the arc representing the percentage where
percentage
It’s the percentage that I want to represent, decimal from 0 to 1.
// Calculate the end Angle of the arc
var currentAngle = percentage*(arcMax-arcMin) + arcMin
// Add another layer of arc to indicate the percentage
var foreground = g.append("path")
.datum({endAngle:currentAngle})
.style("fill"."# 444851")
.attr("d", arc);
Copy the code
- Add a pointer marker to the end of the arc
var tick = g.append("line")
.attr('class'.'gauge-tick')
.attr("x1".0)
.attr("y1", -innerRadius)
.attr("x2".0)
.attr("y2", -(innerRadius + 12)) // Define the line position, default is in the center of the arc, 12 is the length of the pointer
.style("stroke"."#A1A6AD")
.attr('transform'.'rotate('+ angleToDegree(currentAngle) +') ')
Copy the code
The rotate parameter is a degree. Math.PI corresponds to 180, so you need to define an angleToDegree method to convert currentAngle. This is the angleToDegree method in step 3 above
How do I jump the value of this dashboard from one value to another?
Update the arc
Modify the value below the arc:
valueLabel.text(newValue)
Copy the code
Updating the arc is a bit more troublesome. The specific idea is to modify the endAngle of the arc and the transform value of the pointer at the end of the arc. In the implementation process, need to use the API:
selection.transition
:Github.com/d3/d3-trans…transition.attrTween
:Github.com/d3/d3-trans…d3.interpolate
:Github.com/d3/d3-inter…
- Update the arc, where
angle
Is the end Angle of the new arc
// Update the arc and set the gradient effect
foreground.transition()
.duration(750)
.ease(d3.easeElastic) // Set the bouncing effect
.attrTween("d", arcTween(angle));
Copy the code
The arcTween method is defined as follows. It returns a tween (gradient) animation method of the D property that makes an arc gradient from the current Angle to a new Angle.
arcTween(newAngle) {
let self=this
return function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle); // Find an interpolation between the two values
return function(t) {
d.endAngle = interpolate(t); // Calculate interpolation based on transition time t and assign it to endAngle
return arc(d); // Returns the new "d" attribute value
};
};
}
Copy the code
Refer to the Arc Tween comment for a more detailed explanation of this method.
- The principle of updating the pointer at the end of the arc is the same as above, wherein
oldAngle
Is the end Angle of the old arc.
// Update the pointer marker at the end of the arc and set the gradient effect
tick.transition()
.duration(750)
.ease(d3.easeElastic) // Set the bouncing effect
.attrTween('transform'.function(){ // Set the gradient of the "transform" property as arcTween method above
var i = d3.interpolate(angleToDegree(oldAngle), angleToDegree(newAngle)); / / get the interpolation
return function(t) {
return 'rotate('+ i(t) +') '
};
})
Copy the code
Theoretically this way, we’re done. Next, we will implement the landing!
The implementation code
I implemented it based on Vue2
- in
index.html
The introduction ofd3.js
- in
APP.vue
To set my custom style
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = real-time AQI bounced panel style start = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
.gauge-title-0{
font-size: 20px;
fill: # 717171;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-1{
font-size: 20px;
fill: #01e401;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-2{
font-size: 20px;
fill: #ffff00;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-3{
font-size: 20px;
fill: #ff7e00;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-4{
font-size: 20px;
fill: #ff0000;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-5{
font-size: 20px;
fill: #99004c;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-title-6{
font-size: 20px;
fill: #7e0023;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-0{
font-size: 28px;
fill: # 717171;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-1{
font-size: 28px;
fill: #01e401;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-2{
font-size: 28px;
fill: #ffff00;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-3{
font-size: 28px;
fill: #ff7e00;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-4{
font-size: 28px;
fill: #ff0000;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-5{
font-size: 28px;
fill: #99004c;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
.gauge-value-6{
font-size: 28px;
fill: #7e0023;
border-radius: 5px;
padding: 3px;
text-shadow: # 000 0.5 px. 0.5 px. 0.5 px..# 000 0 0.5 px. 0.# 000 -0.5 px. 0 0.# 000 0 -0.5 px. 0;
}
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = real-time AQI bounced panel style end = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
Copy the code
- Custom Components
DynamicDashboard.vue
<template>
<div>
<svg id="myGauge" width="150" height="150" ></svg>
</div>
</template>
<script>
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = defined parameters need to use the dashboard = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// The height and width of SVG can also be obtained from the width and height attributes of SVG
let width = 158;
let height = 158;
// Inner and outer radius of the arc
let innerRadius = 35;
let outerRadius = 60;
// Start and end angles of the arc
let arcMin = -Math.PI * 2 / 3;
let arcMax = Math.PI * 2 / 3;
let valueLabel = 0; // The value of AQI displayed on the dashboard
let foreground; // Represents the length of an AQI arc, which is another arc object. The undefined value defaults to undefined
let tick; // Indicates the pointer to the end of the QI arc. Undefined value defaults to undefined
let arc; // Represents the arc drawing function, undefined value default undefined
let background; // Set the color of the background arc. Undefined values default to undefined
let valueLevel; // Sets the severity level of AQI arcs. Undefined values default to undefined
let percentage; // Indicates the arc length corresponding to the AQI value, which is between 0 and 1. Undefined value defaults to undefined
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = defined parameters need to use the dashboard = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// The Angle turns radians
function angleToDegree(angle) {
return angle * 180 / Math.PI;
}
// Define a tween (gradient) animation method for the d property to gradient an arc from the current Angle to a new Angle.
function arcTween(newAngle) {
// let self = this;
return (d) = > {
const interpolate = d3.interpolate(d.endAngle, newAngle); // Find an interpolation between the two values
return (t) = > {
d.endAngle = interpolate(t); // Calculate interpolation based on transition time t and assign it to endAngle
// Returns the new "d" attribute value
return arc(d);
};
};
}
// Set the AQI dashboard style in the popbox according to the AQI value
/** * isAQI_arc: true to return the value of AQI from 1 to 500, false to return the value of AQI from 1, 2, 3, 4, 5
function AQIStyle(isAQI_arc, value) {
let level = 0;
let AQI_arc = '# 717171';
const valueNum = Number(value);
switch (true) {
case valueNum > 0 && valueNum <= 50:
if (isAQI_arc) {
AQI_arc = '#01e401';
} else {
level = 1;
}
break;
case valueNum > 50 && valueNum <= 100:
if (isAQI_arc) {
AQI_arc = '#ffff00';
} else {
level = 2;
}
break;
case valueNum > 100 && valueNum <= 150:
if (isAQI_arc) {
AQI_arc = '#ff7e00';
} else {
level = 3;
}
break;
case valueNum > 150 && valueNum <= 200:
if (isAQI_arc) {
AQI_arc = '#ff0000';
} else {
level = 4;
}
break;
case valueNum > 200 && valueNum <= 300:
if (isAQI_arc) {
AQI_arc = '#99004c';
} else {
level = 5;
}
break;
case valueNum > 300:
if (isAQI_arc) {
AQI_arc = '#7e0023';
} else {
level = 6;
}
break;
default:
if (isAQI_arc) {
AQI_arc = '# 717171';
} else {
level = 0;
}
break;
}
if (isAQI_arc) {
return AQI_arc;
} else {
returnlevel; }}/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = set dashboard start = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function drawDashboard_AQI(info) {
arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius).startAngle(arcMin);
// Get the DOM element to manipulate
let svg = d3.select('#myGauge');
// Set attributes for the DOM element
let g = svg.append('g').attr('transform'.'translate(' + width / 2 + ', ' + height / 2 + ') ');
// Add the value displayed on the dashboard, declare a variable because it will be updated later
valueLabel = g.append('text').attr('class'.'gauge-value-' + AQIStyle(false, info.AQI))
.style('alignment-baseline'.'central') // Align relative to the parent element
.style('text-anchor'.'middle') // Text anchor point, centered
.attr('y'.0) // The distance to the center
.text(The '-');
// Add the level of the values displayed on the dashboard
valueLevel = g.append('text').attr('class'.'gauge-title-' + AQIStyle(false, info.AQI))
.style('alignment-baseline'.'central') // Align relative to the parent element
.style('text-anchor'.'middle') // Text anchor point, centered
.attr('y'.45) // The distance to the center
.text(The '-'); // There is no unit yet
// Add background arc
background = g.append('path')
.datum({ endAngle: arcMax }) // Pass the endAngle argument to the arc method
.style('fill'.'# 717171')
.attr('d', arc);
// Calculate the end Angle of the arc
percentage = 0; // percentage is the percentage to be represented. It is a decimal from 0 to 1.
const currentAngle = (percentage * (arcMax - arcMin) + arcMin);
// Add another layer of arc to indicate the percentage
foreground = g.append('path')
.datum({ endAngle: currentAngle })
.style('fill', AQIStyle(true, info.AQI))
.attr('d', arc);
tick = g.append('line')
.attr('class'.'gauge-tick')
.attr('x1'.0)
.attr('y1', -innerRadius)
.attr('x2'.0)
.attr('y2', -(innerRadius + 32)) // Define the line position, default is in the center of the arc, 12 is the length of the pointer
.style('stroke'.'#A1A6AD')
.attr('transform'.'rotate(' + angleToDegree(currentAngle) + ') ');
}
/ / = = = = = = = = = = = = = = = = = = = = = = = = = = = = = set dashboard end = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
// Preload open dialog box
function initPopAirAQI(info) {
// Draw an arc
drawDashboard_AQI(info);
setTimeout(() = > {
let endAngle_AQI = 1;
// if (info.AQI == undefined || info.AQI === '--') {
// endAngle_AQI = 0;
// } else {
// endAngle_AQI = (Number(info.AQI) / 500) <= 1 ? (Number(info.AQI) / 500) : 1; // The arc length corresponding to the aQI value
// }
let newAngle = endAngle_AQI * (arcMax - arcMin) + arcMin; // End Angle of the new arc
valueLabel.text(info.AQI); // Set the AQI value
valueLevel.text(info.Level); // Set the level of AQI
// Update the arc and set the gradient effect
foreground.transition()
.duration(750)
.ease(d3.easeElastic) // Set the bouncing effect
.attrTween('d', arcTween(newAngle)); // newAngle: end Angle of the new arc
// Update the pointer marker at the end of the arc and set the gradient effect
tick.transition()
.duration(750)
.ease(d3.easeElastic) // Set the bouncing effect
.attrTween('transform'.() = > { // Set the gradient of the "transform" property as arcTween method above
// 0: is the end Angle of the old arc. NewAngle: End Angle of the new arc
const i = d3.interpolate(angleToDegree(0), angleToDegree(newAngle)); / / get the interpolation
return (t) = > {
return 'rotate(' + i(t) + ') ';
};
});
}, 1000);
}
export default {
data() {
return {
info: {
AQI: 0.Level: 'best',}}; },methods: {},mounted() {
this.$event.$on('AqiData'.(v) = > { // The arrow function must be used because this accepts the passed value
this.info = v;
// console.log(this.info);
setTimeout(() = > {
initPopAirAQI(this.info);
}, 250);
});
},
created() {
// The bus unbinds the previous event before receiving it
this.$event.$off('AqiData'); }};</script>
<style lang="scss" scoped>
</style>
Copy the code
Note:
- because
d3.js
Is in theindex.html
If you type code to set syntax checking, then in this component,d3
This variable it’s going to give you a bang for your buck and prompt you to say thisd3
Undefined, never mind that, the code will work fine if you make a strictEslint detection
So you’re using itd3
Add a separate one to itSkip comments that esLint detects
Can be
- You can see that I’m finally passing
bus
Because of business needs, I want to display this component in a popup, and the popup is based onleaflet
To do it. Yeah, that’s rightleaflet
The map guy. My project iswebgis
The items I found traditionalprops
The value andvuex
I couldn’t get the values through, and then I found outbus
Yes, so I’ll use it herebus
To do.
The cartridge component extends through vue.extend (cartridge component); Method loading, and then through the Leaflet popup method loaded in, and this dashboard component is loaded in the frame component, maybe Vue extend() and Leaflet API between some THINGS I haven’t understood, only bus can go through, later free to study
If you’re in a normal situation, THEN I suggest you use props or vuex
Now that you’ve seen this, please give me a thumbs up before you go, because your thumbs up means a lot to me
Refer to the reading
Feel the guidance of Evelynzzz big man’s H5 implementation