可缩放矢量图形,即SVG,是W3C XML的分支语言之一,用于标记可缩放的矢量图形。目前SVG在Firefox、Opera、Webkit浏览器、IE等浏览器中已经部分实现。
整体效果如下图所示:
一、创建一个SVG,并设置宽420px、高316px。
<svg
width="420px"
height="316px"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
</svg>
注意:用transform="translate(210, 158)"将svg中所有元素的中心点设为数学坐标轴原点,以便于后面进行坐标点计算。
二、圆
使用
circle元素绘制一个圆形。
r 圆的半径
cx 圆心的x位置
cy 圆心的y位置
首先画一个圆心坐标为(0, 0),圆半径为r = 124的正圆。
使用fill="#F2FDFF"设置填充色。
<circle fill="#F2FDFF" cx="0" cy="0" r="124" transform="translate(210, 158)"/>
三、分值进度条
弧形命令
A rx ry x-axis-rotation large-arc-flag sweep-flag x y是创建SVG曲线的命令。
前两个参数rx ry:分别是x轴半径和y轴半径;
参数x-axis-rotation:弧形的x轴旋转角度;
参数large-arc-flag:表示弧线角度(0表示小角度弧,1表示大角度弧);
参数sweep-flag:表示弧线的方向(0表示逆时针,1表示顺时针);
最后两个参数是弧形的终点。
使用弧形命令画进度条关键之处在于计算出弧形的终点位置:
设得分为m,总分max = 100,则角度Q = 180 - m * 180 / max;
设分值进度条端点的横坐标值为x,纵坐标值为y,则可知:
y/x=tanQ
x*x + y*y= r*r = 124*124
由上可分别求得x,y的值:
// 定义计算坐标值函数 Q角度 r半径
function calculateXy(Q, r) {
if (Q === 0) {
return { x: r, y: 0 };
}
if (Q === 180) {
return { x: -r, y: 0 };
}
let tanQ = Math.tan((2 * Math.PI * Q) / 360), // 倾斜角度的正切值
y = Math.abs(Math.sqrt(1 / (tanQ * tanQ + 1)) * r * tanQ), // y始终大于0,所以取绝对值。
x = y / tanQ;
return { x, y };
}
假设得分m=30,通过计算即可得:
Q = 126
tanQ = -1.3763819204711738
y = 100.31810730249349
x = -72.88537128426665
/* <defs>标签在svg中用来存放样式段落;*/
/*<linearGradient>为可通过id属性被其它元素引用的线性渐变色节点*/
<defs>
<linearGradient id="strokeGradient">
<stop offset="0%" stop-color="#8FEAE9" />
<stop offset="100%" stop-color="#26A7DE" />
</linearGradient>
</defs>
/* web规范中,往上偏离元素中心点,top值<0,所以弧形曲线的终点坐标为(x, -y)*/
/* M-124 0表示弧形曲线的起点坐标 */
/* stroke-width="18"设置曲线的宽度 */
<path
:d="`M-124 0 A 124 124, 0, 0, 1, ${x} ${-y}`"
stroke-width="18"
stroke="url(#strokeGradient)"
stroke-linecap="round"
fill="transparent"
transform="translate(210, 158)"
/>
四、分值进度条的端点
<circle stroke="#00D3CA" fill="#fff" :cx="x" :cy="-y" r="10" transform="translate(210, 158)"/>
五、表盘刻度
刻度线就是在两个点之间画直线。首先是“Move to”命令,M,前面出现过,它需要两个参数,分别是需要移动到的点的x轴和y轴的坐标。画直线常用的是“Line to”命令,
L,L需要两个参数,分别是一个点的x轴和y轴坐标,L命令将会在当前位置和新位置(L前面画笔所在的点)之间画一条线段。
共计要画25个刻度线,从右到左第n个刻度线的倾斜角度为n*(180°/24),
// 计算路径
let path = "";
for (let i = 0; i < 25; i++) {
let deg = i * (180 / 24),
// 刻度线外端:弧形半径为100
x1 = calculateXy(deg, 100).x,
y1 = calculateXy(deg, 100).y,
// 刻度线内端:长线弧形半径为88,长线弧形半径为80
r = i % 4 === 0 ? 80 : 88,
x2 = calculateXy(deg, r).x,
y2 = calculateXy(deg, r).y;
path += `M${x1} ${-y1} L${x2} ${-y2} `;
}
this.path = path;
<path transform="translate(210, 158)" :d="path" stroke="#98E1DE" />
具体数值如下图:
六、表盘中心的不规则三角形
用三个二次贝塞尔曲线Q画三角形。
Q x1 y1, x y (or q dx1 dy1, dx dy)需要两组参数,控制点坐标和终点坐标。
// n值用来调节三角形大小
<path
transform="translate(210, 158)"
:d="`M${n*-32.5} ${n*-17.03} Q${n*0} ${n*-64.12} ${n*32.5} ${n*-17.03} Q${n*59} ${n*34.06} ${n*0} ${n*34.06} Q${n*-59} ${n*34.06} ${n*-32.5} ${n*-17.03}`"
fill="rgba(0,211,202,0.23)"
/>
// n值用来调节三角形大小
<path
transform="translate(210, 158)"
:d="`M${-n*-32.5} ${-n*-17.03} Q${-n*0} ${-n*-64.12} ${-n*32.5} ${-n*-17.03} Q${-n*59} ${-n*34.06} ${-n*0} ${-n*34.06} Q${-n*-59} ${-n*34.06} ${-n*-32.5} ${-n*-17.03}`"
fill="rgba(0,147,211,0.19)"
/>
七、添加分数
<text
x="0"
y="10"
font-size="28"
text-anchor="middle"
fill="white"
transform="translate(210, 158)"
>
{{m}}
</text>
八、动画效果
// 完整代码
<svg
width="420px"
height="316px"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id="strokeGradient">
<stop offset="0%" stop-color="#8FEAE9" />
<stop offset="100%" stop-color="#26A7DE" />
</linearGradient>
</defs>
<circle
transform="translate(210, 158)"
fill="#F2FDFF"
cx="0"
cy="0"
r="124"
/>
<path
transform="translate(210, 158)"
:d="`M-124 0 A 124 124, 0, 0, 1, ${x} ${-y}`"
stroke-width="18"
stroke="url(#strokeGradient)"
stroke-linecap="round"
fill="transparent"
/>
<circle
transform="translate(210, 158)"
stroke="#00D3CA"
fill="#fff"
:cx="x"
:cy="-y"
r="10"
/>
<path transform="translate(210, 158)" :d="path" stroke="#98E1DE" />
<path
transform="translate(210, 158)"
:d="`M${n*-32.5} ${n*-17.03} Q${n*0} ${n*-64.12} ${n*32.5} ${n*-17.03} Q${n*59} ${n*34.06} ${n*0} ${n*34.06} Q${n*-59} ${n*34.06} ${n*-32.5} ${n*-17.03}`"
fill="rgba(0,211,202,0.23)"
/>
<path
transform="translate(210, 158)"
:d="`M${-n*-32.5} ${-n*-17.03} Q${-n*0} ${-n*-64.12} ${-n*32.5} ${-n*-17.03} Q${-n*59} ${-n*34.06} ${-n*0} ${-n*34.06} Q${-n*-59} ${-n*34.06} ${-n*-32.5} ${-n*-17.03}`"
fill="rgba(0,147,211,0.19)"
/>
<text
x="0"
y="10"
transform="translate(210, 158)"
:font-size="('' + totalScore).indexOf('-9000') > -1 ? 20 : 28"
text-anchor="middle"
fill="white"
>
{{m}}
</text>
</svg>
export default {
data() {
return {
n: 1.5, // 不规则三角形放大比例
m: 30, // 得分
path: "", // 表盘刻度线路径
x: -124, // 得分进度条起点x
y: 0 // 得分进度条起点y
};
},
created() {
let max = 100, // 总分
r = 124, // 半径
angle = 180 - (this.m * 180) / max, // 角度
Q = 180; // 初始角度
// 添加动效
this.timer = window.setInterval((_) => {
if (Q < angle) {
window.clearInterval(this.timer);
}
Q -= 1;
this.x = calculateXy(Q, r).x;
this.y = calculateXy(Q, r).y;
}, 10);
let path = "";
for (let i = 0; i < 25; i++) {
let deg = i * (180 / 24),
// 刻度线外端:弧形半径为100
x1 = calculateXy(deg, 100).x,
y1 = calculateXy(deg, 100).y,
// 刻度线内端:长线弧形半径为88,长线弧形半径为80
r = i % 4 === 0 ? 80 : 88,
x2 = calculateXy(deg, r).x,
y2 = calculateXy(deg, r).y;
path += `M${x1} ${-y1} L${x2} ${-y2} `;
}
this.path = path;
function calculateXy(Q, r) {
if (Q === 0) {
return { x: r, y: 0 };
}
if (Q === 180) {
return { x: -r, y: 0 };
}
let tanQ = Math.tan((2 * Math.PI * Q) / 360), // 倾斜角度的正切值
y = Math.abs(Math.sqrt(1 / (tanQ * tanQ + 1)) * r * tanQ), // y始终大于0,所以取绝对值。
x = y / tanQ;
return { x, y };
}
}
};