CSS 揭秘(8)--沿环形路径平移的动画

Posted by liveipool on February 9, 2017

沿环形路径平移的动画

这种动画也是非常的优雅和好玩,可以看看这个实例

我们首先写出最基础的代码:

div.path.m-200
	img.avatar(src="/static/pictures/08685344-11d9-426d-a204-ccabbf7049ba-2060x1236.jpeg")


/* Anything below this is just styling */
.avatar {
	display: block;
	width: 50px;
	height: 50px;
	margin: auto;
	border-radius: 50%;
	overflow: hidden;
}

.path {
	width: 300px; height: 300px;
	padding: 20px;
	margin: 100px auto;
	border-radius: 50%;
	background: #fb3;
}

47.1.png

接下来我们加上旋转动画:

@keyframes spin {
	to {transform: rotate(1turn);}
}

.avatar {
	animation: spin 3s infinite linear;
	transform-origin: 50% 150px;
}

47.2.png
但现在会有一个问题,现在不仅头像会沿着路径转,自己也会转动。

需要两个元素的解决方案

上面的那个问题对于上面的结构来说,并没有太好的解决方法,因此我们需要引入多一个元素:

div.path.m-200
	div.avater
		img(src="/static/pictures/08685344-11d9-426d-a204-ccabbf7049ba-2060x1236.jpeg")

现在的思路是:用内层img的变形来抵消外层avatar的变形效果。因为假如我们对img设置另一个旋转动画,让它以相反的方向自转一周,那么这两层旋转的作用会在img上抵消,只剩下avatar上的环绕动作了:

@keyframes spin {
	to {transform: rotate(1turn);}
}

@keyframes spin-reverse {
	from {transform: rotate(1turn);}
}

.avatar {
	animation: spin 3s infinite linear;
	transform-origin: 50% 150px;
}

.avatar > img {
	animation: spin-reverse 3s infinite linear;
} 

/* Anything below this is just styling */
div, img {
	width: 50px;
	height: 50px;
}

div {
	margin: auto;
	border-radius: 50%;
	overflow: hidden;
}

.path {
	width: 300px; height: 300px;
	padding: 20px;
	margin: 100px auto;
	border-radius: 50%;
	background: #fb3;
}

47.3.png
现在就已经实现了我们想要的动画效果了!

不过,现在的代码显得优点荣誉,不符合DRY原则,用两个动画来实现一个动画也有点浪费,我们可以通过animation-direction属性来优化:

@keyframes spin {
	to {transform: rotate(1turn);}
}

.avatar {
	animation: spin 3s infinite linear;
	transform-origin: 50% 150px;
}

.avatar > img {
	animation: inherit;
	animation-direction: reverse;
} 

优化后的代码就可以简化为上面这样了,现在整个代码和效果都比较优雅了,唯一的一点点遗憾是这种方法多添加了一个元素。

单个元素的解决方案

作者讨论了一下transform-origin和translate()之间的转换关系,如:
47.4.png
47.5.png
但这部分是真没怎么看懂了,这种方法虽然少用了一个HTML元素,难度明显提高了一截,不知这种成本是否划算。 最后附上完整的单元素解决方案的代码:

@keyframes spin {
	from {
		transform: rotate(0turn)
		           translateY(-150px) translateY(50%)
		           rotate(1turn)
	}
	to {
		transform: rotate(1turn)
		           translateY(-150px) translateY(50%)
		           rotate(0turn);
	}
}


.avatar {
	animation: spin 3s infinite linear;
}

/* Anything below this is just styling */

.avatar {
	display: block;
	width: 50px;
	margin: calc(50% - 25px) auto 0;
	border-radius: 50%;
	overflow: hidden;
}

.path {
	width: 300px; height: 300px;
	padding: 20px;
	margin: 100px auto;
	border-radius: 50%;
	background: #fb3;
}