环形文字旋转的实现

691 阅读2分钟

需求如下图:

智慧地球UI1动图2.gif

中间的地球是个gif图哈,但是我记得通过无缝滚动也可以实现地球滚动,这里主要看的是地球周围的滚动文字,要实现这个效果,有两种方法:

  1. SVG直接绘制
  2. CSS+JS

因为我这次用的是uniapp,它老是把SVG标签内的 text 标签解析成 uni-text,跟个癫公一样,因此这里只能放弃了SVG的方案。

SVG方案

代码如下:

<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属性,最后给容器加上旋转动画即可。

实现代码

模板代码

<div class="container">
	环形文字环形文字
</div>

样式代码

// 容器样式
.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);
	}
}

逻辑代码

首先获取容器元素,然后复制其内部的文本,再然后将内部文本清空:

const container = document.querySelector('.container')
const textContent = container.innerText
container.innerText = ''

接着遍历保存的文本内容,逐个字的遍历,每次遍历都会创建一个span元素,然后通过下标计算出其rotate的值,然后将span元素添加到容器元素中:

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)
}

此时在页面上就可以看到效果了:

image.png

uniapp中实现

如果是在uniapp中,可以像下面这样实现(因为我是要在移动端使用,因此尽量不操作DOM)。

模板代码

<view class="text-container">
	<template v-for="(item,index) in list">
		<view v-if="item==='n'" class="text" :style="getRoateStyle(index)">&nbsp;</view>
		<view v-else class="text" :style="getRoateStyle(index)">{{item}}</view>
	</template>
</view>

逻辑代码

首先定义一个list变量,里面存放要显示的字符:

list: "nnn碎冰岛nnn雪贼岛nnn海风岛nnn费力墩补给站nnn哈尔滨补给站nnn雷霆军港nnn萨拉补给站"

这个字符串中的n是用来占位的,因为需要预留一些位置给图片。

然后定义一个getRoateStyle方法,传入index,就会返回一个计算好的动态style,如下:

getRoateStyle(index) {
	const len = this.list.length
	const r = (360 / len) * index
	return {
		transform: `rotate(${r}deg)`
	}
}

此时的效果如下:

image.png

然后就到最后一步,把小岛的图片放到缝隙中,我本来是在遍历list的时候把图片给遍历出来的,结果发现,每个小岛图片的top和left值是不同的,而且是不成规律的,因此只能手写这些小岛的样式了,在模板 .text-container 中加入一个小岛的图片:

<view class="text-container">
	<!-- ... -->
	<image class="island-icon sb-island" src="/static/imgs/smartEarth/islandIcons/island1.gif" mode="aspectFill"></image>
</view>

然后手动调整位置:

.island-icon {
	width: 70rpx;
	height: 70rpx;
	position: absolute;
}

// 碎冰岛
.sb-island {
	top: -20rpx;
	left: 210rpx;
}

最后实现效果如下:

20231110_164333.gif