<line id="hourHand" x1="50" y1="50" x2="50" y2="24" hourHandPlace>
<animateTransform xlink:href="#hourHand" attributeName="transform" attributeType="XML" type="rotate"
from="hourHandTransFrom" to="hourHandTransTo" dur="43200s" repeatCount="indefinite" />
</line>
<line id="minHand" x1="50" y1="50" x2="50" y2="20" minHandPlace>
<animateTransform xlink:href="#minHand" attributeName="transform" attributeType="XML" type="rotate"
from="minHandTransFrom" to="minHandTransTo" dur="3600s" repeatCount="indefinite" />
</line>
<line id="secHand" x1="50" y1="50" x2="50" y2="16" secHandPlace>
<animateTransform xlink:href="#secHand" attributeName="transform" attributeType="XML" type="rotate"
from="secHandTransFrom" to="secHandTransTo" dur="60s" repeatCount="indefinite" />
</line>
其中 hourHandPlace hourHandTransFrom hourHandTransTo 是字符串模板,分别表示时钟时针的tansform变换初始角度、
动画初始值,动画的结束值
通过服务端模板替换后,转换为
<line id="hourHand" x1="50" y1="50" x2="50" y2="24" transform="rotate(188,50,50)">
<animateTransform xlink:href="#hourHand" attributeName="transform" attributeType="XML" type="rotate"
from="188 50 50" to="548 50 50" dur="43200s" repeatCount="indefinite" />
</line>
分钟和秒钟同理
let svg = readFileSync(resolve('public', 'clock.svg'), 'utf-8');
const now = new Date(); //当前时间
const min = now.getMinutes(); //分钟
const sec = now.getSeconds(); //秒
const hour = (now.getHours() % 12) + min / 60; //转换成可以在时钟上显示的时间
const hourAngle = hour * 30; //每30°表示一个小时
const minAngle = min * 6; //每6°表示一分钟
const secAngle = sec * 6; //每6°表示一秒
const setTransform = (str, angle) => {
const attr = `transform="rotate(${angle},50,50)"`;
svg = svg.replace(str, attr);
};
[
['hourHandPlace', hourAngle],
['minHandPlace', minAngle],
['secHandPlace', secAngle],
].forEach((ele) => {
setTransform(ele[0], ele[1]);
});
[
['hourHandTrans', hourAngle],
['minHandTrans', minAngle],
['secHandTrans', secAngle],
].forEach((ele: [string, number]) => {
const from = ele[1] + ' 50 50';
const to = ele[1] + 360 + ' 50 50';
const fromKey = ele[0] + 'From';
const toKey = ele[0] + 'To';
svg = svg.replace(fromKey, from);
svg = svg.replace(toKey, to);
});
return svg;
<svg id="clock" viewBox="0 0 100 100" width="500" height="500" role="img" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
#clock {
stroke: black;
stroke-linecap: round;
fill: #eef;
}
#face {
stroke-width: 3px;
}
#ticks {
stroke-width: 2;
}
#hourHand {
stroke-width: 5px;
}
#minHand {
stroke-width: 3px;
}
#secHand {
stroke-width: 1px;
}
#numbers {
font-family: sans-serif;
font-size: 7pt;
font-weight: bold;
text-anchor: middle;
stroke: none;
fill: black;
}
</style>
<defs>
<filter id=" shadow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="sourceAlpha" stDeviation="1" result="blur" />
<feOffset in="blur" dx="1" dy="1" result="shadow" />
<feMerge>
<feMergeMode in="SourceGraphic" />
<feMergeMode in="shadow" />
</feMerge>
</filter>
</defs>
<circle id="face" cx="50" cy="50" r="45" />
<g id="ticks">
<line x1="50" y1="5.000" x2="50.00" y2="10.00" />
<line x1="72.50" y1="11.03" x2="70.00" y2="15.36" />
<line x1="88.97" y1="27.50" x2="84.64" y2="30.00" />
<line x1="95.00" y1="50.00" x2="90.00" y2="50.00" />
<line x1="88.97" y1="72.50" x2="84.64" y2="70.00" />
<line x1="72.50" y1="88.97" x2="70.00" y2="84.64" />
<line x1="50.00" y1="95.00" x2="50.00" y2="90.00" />
<line x1="27.50" y1="88.97" x2="30.00" y2="84.64" />
<line x1="11.03" y1="72.50" x2="15.36" y2="70.00" />
<line x1="5.000" y1="50.00" x2="10.00" y2="50.00" />
<line x1="11.03" y1="27.50" x2="15.36" y2="30.00" />
<line x1="27.50" y1="11.03" x2="30.00" y2="15.36" />
</g>
<g id="numbers">
<text x="50" y="18">12</text><text x="85" y="53">3</text>
<text x="50" y="88">6</text><text x="15" y="53">9</text>
</g>
<g id="hands">
<line id="hourHand" x1="50" y1="50" x2="50" y2="24" hourHandPlace>
<animateTransform xlink:href="#hourHand" attributeName="transform" attributeType="XML" type="rotate"
from="hourHandTransFrom" to="hourHandTransTo" dur="43200s" repeatCount="indefinite" />
</line>
<line id="minHand" x1="50" y1="50" x2="50" y2="20" minHandPlace>
<animateTransform xlink:href="#minHand" attributeName="transform" attributeType="XML" type="rotate"
from="minHandTransFrom" to="minHandTransTo" dur="3600s" repeatCount="indefinite" />
</line>
<line id="secHand" x1="50" y1="50" x2="50" y2="16" secHandPlace>
<animateTransform xlink:href="#secHand" attributeName="transform" attributeType="XML" type="rotate"
from="secHandTransFrom" to="secHandTransTo" dur="60s" repeatCount="indefinite" />
</line>
</g>
</svg>
export class ClockController {
@Get('clock.svg')
@Header('Content-Type', 'image/svg+xml')
svgClock() {
//获取表示时钟和分针的svg元素
let svg = readFileSync(resolve('public', 'clock.svg'), 'utf-8');
const now = new Date(); //当前时间
const min = now.getMinutes(); //分钟
const sec = now.getSeconds(); //秒
const hour = (now.getHours() % 12) + min / 60; //转换成可以在时钟上显示的时间
const hourAngle = hour * 30; //每30°表示一个小时
const minAngle = min * 6; //每6°表示一分钟
const secAngle = sec * 6; //每6°表示一秒
const setTransform = (str, angle) => {
const attr = `transform="rotate(${angle},50,50)"`;
svg = svg.replace(str, attr);
};
[
['hourHandPlace', hourAngle],
['minHandPlace', minAngle],
['secHandPlace', secAngle],
].forEach((ele) => {
setTransform(ele[0], ele[1]);
});
[
['hourHandTrans', hourAngle],
['minHandTrans', minAngle],
['secHandTrans', secAngle],
].forEach((ele: [string, number]) => {
const from = ele[1] + ' 50 50';
const to = ele[1] + 360 + ' 50 50';
const fromKey = ele[0] + 'From';
const toKey = ele[0] + 'To';
svg = svg.replace(fromKey, from);
svg = svg.replace(toKey, to);
});
return svg;
}
}