需求如下图:
中间的地球是个gif图哈,但是我记得通过无缝滚动也可以实现地球滚动,这里主要看的是地球周围的滚动文字,要实现这个效果,有两种方法:
- SVG直接绘制
- CSS+JS
因为我这次用的是uniapp,它老是把SVG标签内的 text
标签解析成 uni-text
,跟个癫公一样,因此这里只能放弃了SVG的方案。
SVG方案
代码如下:
html
<svg width="400" height="400">
<path id="circlePath" d="M200,50 A150,150 0 1,1 200,349.999" fill="none" />
<text>
<textPath xlink:href="#circlePath">
环形文本
</textPath>
</text>
</svg>
CSS+JS
实现思路
首先是一个容器view,设置为相对定位,然后内部每一个字都是一个text元素,通过绝对定位定位到同一个位置(也就是每个字都重叠在一起),然后通过js代码控制每个text元素的transform属性,最后给容器加上旋转动画即可。
实现代码
模板代码
html
<div class="container">
环形文字环形文字
</div>
样式代码
scss
// 容器样式
.container {
width: 430px;
height: 430px;
position: relative;
text-align: center;
animation: spin 20s linear infinite;
// 文字样式
.text {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
font-size: 25px;
}
}
// 旋转动画
@keyframes spin {
0% {
transform: rotate(360deg);
}
100% {
transform: rotate(0);
}
}
逻辑代码
首先获取容器元素,然后复制其内部的文本,再然后将内部文本清空:
js
const container = document.querySelector('.container')
const textContent = container.innerText
container.innerText = ''
接着遍历保存的文本内容,逐个字的遍历,每次遍历都会创建一个span元素,然后通过下标计算出其rotate的值,然后将span元素添加到容器元素中:
js
for (let i = 0; i < textContent.length; i++) {
const span = document.createElement('span')
span.className = 'text'
span.innerText = textContent[i]
const r = i * 360 / textContent.length
span.style.transform = `rotate(${r}deg)`
container.appendChild(span)
}
此时在页面上就可以看到效果了:
uniapp中实现
如果是在uniapp中,可以像下面这样实现(因为我是要在移动端使用,因此尽量不操作DOM)。
模板代码
vue
<view class="text-container">
<template v-for="(item,index) in list">
<view v-if="item==='n'" class="text" :style="getRoateStyle(index)"> </view>
<view v-else class="text" :style="getRoateStyle(index)">{{item}}</view>
</template>
</view>
逻辑代码
首先定义一个list变量,里面存放要显示的字符:
js
list: "nnn碎冰岛nnn雪贼岛nnn海风岛nnn费力墩补给站nnn哈尔滨补给站nnn雷霆军港nnn萨拉补给站"
这个字符串中的n是用来占位的,因为需要预留一些位置给图片。
然后定义一个getRoateStyle方法,传入index,就会返回一个计算好的动态style,如下:
js
getRoateStyle(index) {
const len = this.list.length
const r = (360 / len) * index
return {
transform: `rotate(${r}deg)`
}
}
此时的效果如下:
然后就到最后一步,把小岛的图片放到缝隙中,我本来是在遍历list的时候把图片给遍历出来的,结果发现,每个小岛图片的top和left值是不同的,而且是不成规律的,因此只能手写这些小岛的样式了,在模板 .text-container
中加入一个小岛的图片:
vue
<view class="text-container">
<!-- ... -->
<image class="island-icon sb-island" src="/static/imgs/smartEarth/islandIcons/island1.gif" mode="aspectFill"></image>
</view>
然后手动调整位置:
css
.island-icon {
width: 70rpx;
height: 70rpx;
position: absolute;
}
// 碎冰岛
.sb-island {
top: -20rpx;
left: 210rpx;
}
最后实现效果如下: