小火车时钟

Published on
/
/趣玩前端
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>小火车时钟 🚂🕒</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      body {
        background: radial-gradient(circle, grey, #030);
        margin: 0 auto;
        height: 100vh;
      }
      #trainClockArea {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 95%;
        max-width: 400px;
        height: 95%;
        max-height: 400px;
      }
      #trainClockArea > #trainClock {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        height: 100%;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="trainClockArea"></div>

    <script>
      function createTrainClock() {
        // Get the current time
        const now = new Date();
        // Seconds passed in the current day
        const secondsInDay =
          now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds();
        // Seconds passed in the current hour
        const secondsInHour = now.getMinutes() * 60 + now.getSeconds();
        // Seconds passed in the current minute
        const secondsInMinute = now.getSeconds();
        // 	console.log(`Seconds passed in the current day: ${secondsInDay}`);
        // 	console.log(`Seconds passed in the current hour: ${secondsInHour}`);
        // 	console.log(`Seconds passed in the current minute: ${secondsInMinute}`);

        const svg = `<svg id="trainClock" viewBox="0 0 100 100">
	<defs>
		<path id="trackSec" fill="none" d="M 50 7.5 A 1 1 0 0 1 50 92.5 A 1 1 0 0 1 50 7.5" />
		<path id="trackMin" fill="none" d="M 50 20 A 1 1 0 0 1 50 80 A 1 1 0 0 1 50 20" />
		<path id="trackHour" fill="none" d="M 50 32.5 A 1 1 0 0 1 50 67.5 A 1 1 0 0 1 50 32.5" />
		<filter id="blur" x="-100%" y="-100%" width="300%" height="300%">
      <feTurbulence type="turbulence" baseFrequency="0.1" numOctaves="9" result="turbulence" seed="69" />
      <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="5" />
    </filter>
		<filter id="blur1" x="-100%" y="-100%" width="300%" height="300%">
			<feGaussianBlur in="SourceGraphic" stdDeviation="1" />
		</filter>
		<filter id="blur2" x="-100%" y="-100%" width="300%" height="300%">
			<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
		</filter>
		<radialGradient id="grassGrad1" gradientUnits="userSpaceOnUse">
			<stop offset="50%" stop-color="#0b0" />
			<stop offset="75%" stop-color="forestgreen" />
			<stop offset="100%" stop-color="darkgreen" />
		</radialGradient>
	</defs>
	<circle cx="50" cy="50" r="50" fill="url(#grassGrad1)" />
	<circle cx="50" cy="50" r="30" fill="none" stroke="url(#grassGrad2)" stroke-width="13" />
	<circle cx="50" cy="50" r="17.5" fill="none" stroke="url(#grassGrad3)" stroke-width="13" />
	<circle cx="50" cy="50" r="42.5" fill="none" stroke="#666" stroke-width="7.5" filter="url(#blur2)" />
	<use href="#trackSec" stroke="tan" stroke-width="10" stroke-dasharray="2.22511833333" stroke-dashoffset="1.11255916667" />
	<circle cx="50" cy="50" r="45" fill="none" stroke="lightgray" />
	<circle cx="50" cy="50" r="40" fill="none" stroke="lightgray" />
	<g id="boxCar1">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 107, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="secBox1" dur="60s" begin="${
      (secondsInMinute - 3.5) * -1 - 60
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackSec" />
		</animateMotion>
	</g>
	<g id="boxCar2">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 122, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="secBox2" dur="60s" begin="${
      (secondsInMinute - 6.9) * -1 - 60
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackSec" />
		</animateMotion>
	</g>
	<g id="boxCar3">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 136, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="secBox3" dur="60s" begin="${
      (secondsInMinute - 10.3) * -1 - 60
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackSec" />
		</animateMotion>
	</g>
	<g id="railCar">
		<ellipse id="fwS" cx="-5" rx="3" ry="4.5" fill="#666" />
		<circle id="csS" r="4" cx="-5" fill="rgb(255, 50, 66)" />
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 50, 66)" />
		<rect height="7" width="14" x="-7" y="-3.5" rx="1" fill="rgb(255, 239, 50)" />
		<circle id="d1S" r="0.8" cx="-3" fill="#666" />
		<circle id="d2S" r="0.8" cx="-0.5" fill="#666" />
		<rect height="9.5" width="7.5" x="0" y="-4.75" rx="1" fill="rgb(255, 50, 66)" />
		<circle id="ss1S" r="2.5" cx="-5.5" fill="darkgrey" />
		<circle id="ss2S" r="1.5" cx="-5.5" fill="#555" />
		<g filter="url(#blur)">
			<circle id="puff1S" r="1.5" cx="-5.5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-8; 15" dur="5s" begin="0s" repeatCount="indefinite" />
			</circle>
			<circle id="puff2S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-1s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-1s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-8; 15" dur="5s" begin="-1s" repeatCount="indefinite" />
			</circle>
			<circle id="puff3S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-8; 15" dur="5s" begin="-2s" repeatCount="indefinite" />
			</circle>
			<circle id="puff4S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-3s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-3s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-8; 15" dur="5s" begin="-3s" repeatCount="indefinite" />
			</circle>
			<circle id="puff5S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-8; 15" dur="5s" begin="-4s" repeatCount="indefinite" />
			</circle>
		</g>
		<animateMotion id="secEngine" dur="60s" begin="-${secondsInMinute}s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackSec" />
		</animateMotion>
	</g>
	<circle cx="50" cy="50" r="30" fill="none" stroke="#666" stroke-width="7.5" filter="url(#blur2)" />
	<use href="#trackMin" stroke="tan" stroke-width="10" stroke-dasharray="3.142035" stroke-dashoffset="1.5710175" />
	<circle cx="50" cy="50" r="32.5" fill="none" stroke="lightgray" />
	<circle cx="50" cy="50" r="27.5" fill="none" stroke="lightgray" />
	<g id="boxCar1M">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 107, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="minBox1" dur="3600s" begin="${
      (secondsInHour - 310) * -1 - 3600
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackMin" />
		</animateMotion>
	</g>
	<g id="boxCar2M">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 122, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="minBox2" dur="3600s" begin="${
      (secondsInHour - 610) * -1 - 3600
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackMin" />
		</animateMotion>
	</g>
	<g id="railCarM">
		<ellipse id="fwM" cx="-5" rx="3" ry="4.5" fill="#666" />
		<circle id="csM" r="4" cx="-5" fill="rgb(255, 50, 66)" />
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 50, 66)" />
		<rect height="7" width="14" x="-7" y="-3.5" rx="1" fill="rgb(50, 220, 255)" />
		<circle id="d1M" r="0.8" cx="-3" fill="#666" />
		<circle id="d2M" r="0.8" cx="-0.5" fill="#666" />
		<rect height="9.5" width="7.5" x="0" y="-4.75" rx="1" fill="rgb(255, 50, 66)" />
		<circle id="ss1M" r="2.5" cx="-5.5" fill="darkgrey" />
		<circle id="ss2M" r="1.5" cx="-5.5" fill="#555" />
		<g filter="url(#blur)">
			<circle id="puff1S" r="1.5" cx="-5.5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 10" dur="5s" begin="0s" repeatCount="indefinite" />
			</circle>
			<circle id="puff2S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-1s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-1s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 10" dur="5s" begin="-1s" repeatCount="indefinite" />
			</circle>
			<circle id="puff3S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 10" dur="5s" begin="-2s" repeatCount="indefinite" />
			</circle>
			<circle id="puff4S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-3s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-3s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 10" dur="5s" begin="-3s" repeatCount="indefinite" />
			</circle>
			<circle id="puff5S" r="1.5" cx="-5" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 10" dur="5s" begin="-4s" repeatCount="indefinite" />
			</circle>
		</g>
		<animateMotion id="minEngine" dur="3600s" begin="-${secondsInHour}s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackMin" />
		</animateMotion>
	</g>

	<circle cx="50" cy="50" r="17.5" fill="none" stroke="#666" stroke-width="7.5" filter="url(#blur2)" />
	<use href="#trackHour" stroke="tan" stroke-width="10" stroke-dasharray="4.5821375" stroke-dashoffset="2.29106875" />
	<circle cx="50" cy="50" r="15" fill="none" stroke="lightgray" />
	<circle cx="50" cy="50" r="20" fill="none" stroke="lightgray" />
	<g id="boxCar1H">
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 107, 50)" />
		<rect height="5" width="10" x="-5" y="-2.5" rx="0.6" fill="rgba(0,0,0,0.2)" />
		<animateMotion id="hrBox1" dur="43200s" begin="${
      (secondsInDay - 6500) * -1 - 43200
    }s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackHour" />
		</animateMotion>
	</g>
	<g id="railCarH">
		<ellipse id="fwH" cx="-5" rx="3" ry="4.5" fill="#666" />
		<circle id="csH" r="4" cx="-5" fill="rgb(255, 50, 66)" />
		<rect height="8" width="14" x="-7" y="-4" rx="1" fill="rgb(255, 50, 66)" />
		<rect height="7" width="14" x="-7" y="-3.5" rx="1" fill="rgb(74, 255, 50)" />
		<circle id="d1H" r="0.8" cx="-3" fill="#666" />
		<circle id="d2H" r="0.8" cx="-0.5" fill="#666" />
		<rect height="9.5" width="7.5" x="0" y="-4.75" rx="1" fill="rgb(255, 50, 66)" />
		<circle id="ss1H" r="2.5" cx="-5.5" fill="darkgrey" />
		<circle id="ss2H" r="1.5" cx="-5.5" fill="#555" />
		<g filter="url(#blur)">
			<circle id="puff1H" r="1.5" cx="-7" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="0s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 7" dur="5s" begin="0s" repeatCount="indefinite" />
			</circle>
			<circle id="puff3H" r="1.5" cx="-7" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-2s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 7" dur="5s" begin="-2s" repeatCount="indefinite" />
			</circle>
			<circle id="puff5H" r="1.5" cx="-7" cy="-1" fill="#fff">
				<animate attributeName="r" values="0; 4; 2" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="opacity" values="1; 0.8; 0" dur="5s" begin="-4s" repeatCount="indefinite" />
				<animate attributeName="cx" values="-7; 7" dur="5s" begin="-4s" repeatCount="indefinite" />
			</circle>
		</g>
		<animateMotion id="hrEngine" dur="43200s" begin="-${secondsInDay}s" repeatCount="indefinite" rotate="auto-reverse">
			<mpath href="#trackHour" />
		</animateMotion>
	</g>
</svg>`;

        const wrapper = document.createElement('div');
        wrapper.insertAdjacentHTML('afterbegin', svg);
        const doc = wrapper.firstChild;
        document.getElementById('trainClockArea').appendChild(doc);
      }

      createTrainClock();

      // refresh it if we leave the page
      document.addEventListener('visibilitychange', () => {
        if (!document.hidden) {
          document.getElementById('trainClockArea').replaceChildren();
          createTrainClock();
        }
      });
    </script>
  </body>
</html>