效果來自一款屏保軟件:world-clock,以爲挺有意思的,因而深夜決定本身動手擼一個。javascript
在線預覽:羅盤時鐘css
預覽截圖:java
首先是要將多個文字元素呈圓形排布,以後將圓形以必定的角度定時旋轉來達到效果。git
接下來首先實現如何將文字呈圓形排布,搞定了這個,後面的也就不難了。github
接下來咱們先來一步一步實現最外圍的「秒」,秒有0-59一共60個元素,首先將他們定位在一個正方形div裏。markdown
<template>
<div class="home"> <!-- 秒 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('seconds')"> <span v-for="(item, index) in secondTexts" :key="item" >{{ item }}</span> </div> </div> </div>
</template>
<script> export default { name: 'Home', data() { return { secondTexts: [ '零零', '一秒', '二秒', '三秒', '四秒', '五秒', '六秒', '七秒', '八秒', '九秒', '十秒', '十一秒', '十二秒', '十三秒', '十四秒', '十五秒', '十六秒', '十七秒', '十八秒', '十九秒', '二十秒', '二十一秒', '二十二秒', '二十三秒', '二十四秒', '二十五秒', '二十六秒', '二十七秒', '二十八秒', '二十九秒', '三十秒', '三十一秒', '三十二秒', '三十三秒', '三十四秒', '三十五秒', '三十六秒', '三十七秒', '三十八秒', '三十九秒', '四十秒', '四十一秒', '四十二秒', '四十三秒', '四十四秒', '四十五秒', '四十六秒', '四十七秒', '四十八秒', '四十九秒', '五十秒', '五十一秒', '五十二秒', '五十三秒', '五十四秒', '五十五秒', '五十六秒', '五十七秒', '五十八秒', '五十九秒' ], // 盒子大小 boxSize: { seconds: 580 } } }, methods: { // 設置文字外圍盒子寬高 boxStyle(key) { return { width: this.boxSize[key] + 'px', height: this.boxSize[key] + 'px' } } } } </script>
<style lang="scss" scoped> .home { height: 100%; width: 100%; background-color: #000000; color: #71767D; position: relative; min-width: 800px; min-height: 660px; padding: 20px 0; overflow: hidden; } .box-wrapper { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .circle-box { position: relative; border: 1px solid red; } .circle-box span { white-space: nowrap; font-size: 14px; position: absolute; } </style>
複製代碼
如今文字設置absolute定位後都堆疊在一塊兒了,接下來經過位置計算讓其呈圓形排布。app
從圖中咱們知道,要讓文字排列在圓上,咱們須要知道文字元素到O點(圓心)的橫座標和縱座標,也就是a的高度和b的長度,對應咱們要設置定位的top和left值。oop
半徑咱們是知道的,即盒子寬度的一半 r = 580 / 2 = 290,每一個元素與圓心的夾角c = (360 / 60) * i,據此根據數學公式咱們能夠求出a和b的值。新增代碼以下:動畫
<template>
<div class="home"> <!-- 秒 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('seconds')"> <span v-for="(item, index) in secondTexts" :key="item" :style="spanStyle(boxSize.seconds, secondTexts, index)" >{{ item }}</span> </div> </div> </div>
</template>
<script> export default { name: 'Home', methods: { spanStyle(size, texts, i) { const r = size / 2 // 半徑 const deg = this.getPerDeg(texts) // 元素平均間隔度數 const angle = i * deg // 夾角 const { a, b } = this.getHypotenuse(r, angle) const rotateDeg = deg * i // 文字旋轉角度 return { top: a + r + 'px', left: b + r + 'px', transform: `rotate(${rotateDeg}deg)`, transformOrigin: '0 0' } }, // 元素平均間隔度數 getPerDeg(texts) { return 360 / texts.length }, // 已知角度和斜邊,獲取直角邊 getHypotenuse(long, angle) { // 得到弧度 let radian = 2 * Math.PI / 360 * angle return { a: Math.sin(radian) * long, // 鄰邊 b: Math.cos(radian) * long // 對邊 } } } } </script>
複製代碼
效果不錯,接下來發揮cv大法,把分鐘緯度給弄上去。新增代碼以下:ui
<template>
<div class="home"> <!-- 分 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('minutes', minutesDeg)"> <span v-for="(item, index) in minuteTexts" :key="item" :style="spanStyle(boxSize.minutes, minuteTexts, index)" >{{ item }}</span> </div> </div> <!-- 秒 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('seconds', secondsDeg)"> <span v-for="(item, index) in secondTexts" :key="item" :style="spanStyle(boxSize.seconds, secondTexts, index)" >{{ item }}</span> </div> </div> </div>
</template>
<script> export default { name: 'Home', data() { return { secondTexts: [ '零零', '一秒', '二秒', '三秒', '四秒', '五秒', '六秒', '七秒', '八秒', '九秒', '十秒', '十一秒', '十二秒', '十三秒', '十四秒', '十五秒', '十六秒', '十七秒', '十八秒', '十九秒', '二十秒', '二十一秒', '二十二秒', '二十三秒', '二十四秒', '二十五秒', '二十六秒', '二十七秒', '二十八秒', '二十九秒', '三十秒', '三十一秒', '三十二秒', '三十三秒', '三十四秒', '三十五秒', '三十六秒', '三十七秒', '三十八秒', '三十九秒', '四十秒', '四十一秒', '四十二秒', '四十三秒', '四十四秒', '四十五秒', '四十六秒', '四十七秒', '四十八秒', '四十九秒', '五十秒', '五十一秒', '五十二秒', '五十三秒', '五十四秒', '五十五秒', '五十六秒', '五十七秒', '五十八秒', '五十九秒' ], minuteTexts: [ '零零', '一分', '二分', '三分', '四分', '五分', '六分', '七分', '八分', '九分', '十分', '十一分', '十二分', '十三分', '十四分', '十五分', '十六分', '十七分', '十八分', '十九分', '二十分', '二十一分', '二十二分', '二十三分', '二十四分', '二十五分', '二十六分', '二十七分', '二十八分', '二十九分', '三十分', '三十一分', '三十二分', '三十三分', '三十四分', '三十五分', '三十六分', '三十七分', '三十八分', '三十九分', '四十分', '四十一分', '四十二分', '四十三分', '四十四分', '四十五分', '四十六分', '四十七分', '四十八分', '四十九分', '五十分', '五十一分', '五十二分', '五十三分', '五十四分', '五十五分', '五十六分', '五十七分', '五十八分', '五十九分' ], // 盒子大小 boxSize: { seconds: 580, minutes: 440 } } } } </script>
複製代碼
新增代碼以下:
<template>
<div class="home"> <!-- 分 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('minutes', minutesDeg)"> <span v-for="(item, index) in minuteTexts" :key="item" :style="spanStyle(boxSize.minutes, minuteTexts, index)" :class="{'active': index === currentMinutes}" >{{ item }}</span> </div> </div> <!-- 秒 --> <div class="box-wrapper"> <div class="circle-box" :style="boxStyle('seconds', secondsDeg)"> <span v-for="(item, index) in secondTexts" :key="item" :style="spanStyle(boxSize.seconds, secondTexts, index)" :class="{'active': index === currentSeconds}" >{{ item }}</span> </div> </div> </div>
</template>
<script> export default { name: 'Home', data() { return { currentMinutes: 0, // 當前-分鐘 currentSeconds: 0, // 當前-秒 minutesDeg: 0, // 當前-分鐘-轉動角度 secondsDeg: 0, // 當前-面-轉動角度 timer: null // 定時器 } }, mounted() { this.init() }, methods: { init() { const d = new Date() const minutes = d.getMinutes() // 分 const seconds = d.getSeconds() // 秒 // 當前時間 this.currentMinutes = minutes this.currentSeconds = seconds // 角度 this.minutesDeg = this.currentMinutes * this.getPerDeg(this.minuteTexts) this.secondsDeg = this.currentSeconds * this.getPerDeg(this.secondTexts) // 設置定時器 this.timer = setInterval(() => { this.runClock() }, 1000) // 記得清除定時器 this.$once('hook:beforeDestroy', () => { clearInterval(this.timer) }) }, boxStyle(key, deg) { return { // 設置文字外圍盒子寬度、高度 width: this.boxSize[key] + 'px', height: this.boxSize[key] + 'px', // 添加轉動 transform: `rotate(-${deg}deg)` } }, // 元素平均間隔度數 getPerDeg(texts) { return 360 / texts.length }, runClock() { const d = new Date() const minutes = d.getMinutes() // 分 const seconds = d.getSeconds() // 秒 if (this.currentMinutes !== minutes) { this.currentMinutes = minutes this.minutesDeg += this.getPerDeg(this.minuteTexts) } this.currentSeconds = seconds this.secondsDeg += this.getPerDeg(this.secondTexts) } } } </script>
<style lang="scss" scoped> .circle-box { position: relative; // 添加動畫效果 transition: transform 0.4s ease-in-out; } // 激活時文字顏色爲白色 .circle-box span.active { color: #fff; } </style>
複製代碼
至此,整個工做就完成得差很少了,剩餘的月、日、時等緯度也就是依樣畫葫蘆的事,這裏就不一一貼出來了,感興趣的同窗能夠移步GitHub查看完整代碼。
完整代碼地址:GitHub
本文到這裏就結束了,感謝閱讀,碼字不易,歡迎你的點贊👍!!!
本文若是有什麼錯誤的地方,也歡迎評論區指正、交流!