用原生JS和CSS3做一个有趣的cube相册

一、前言

最近以来一直在做混合式开发app,也有一段时间没写博客了,过程中遇到不少的坑,但也因此积累了不少的经验,我会把他们都记录下来和大家分享,比如我用的比较多的一个插件better-scroll,是移动端滚动的一种解决方案,其实这也是我在慕课网花了198大洋购买的vue1.0高仿饿了么app商家模块实战教学所了解到的,不过现在实战教学名字改成了Vue.js 2.5 + cube-ui 重构饿了么 App,感兴趣的同学可以去看看,老师讲的非常不错(嘿嘿,这里为老师打call~)。广告打完了,言归正传,自己看了会better-scroll动量计算的源码,于是就做了个有趣的东西,也就是今天的主角–cube相册`(∩_∩)′

二、准备工作

首先,你得熟悉如何布局立方体,如果你不会的话,好吧,可以点这里 ,我在这篇文章中对于如何布局cube有很详细的阐述。再其次就是会一定的js啦~(如果你也不会的话也木有关系 o(>﹏<)o)

三、开始有趣的cube相册

对于基本的布局可以参见以下代码

HTML部分

<div class='wrapper'>
	<div class='cube-wrapper'>
		<div class="cube-item"></div>
		<div class="cube-item"></div>
		<div class="cube-item"></div>
		<div class="cube-item"></div>
		<div class="cube-item"></div>
		<div class="cube-item"></div>
	</div>
</div>
<div class='btn'>reset cube</div>

这里解释下,我在最后加了个按钮,他的作用在下面会介绍

CSS部分

html{
	height: 100%;
}
body{
	overflow: hidden;
	height: 100%;
	margin: 0;
	background-image: linear-gradient(120deg, #E55D87, #5FC3E4);
}
.wrapper{
	perspective: 800px;
}
.wrapper:hover{
	cursor: pointer;
}
.cube-wrapper{
	transform-style: preserve-3d;
	transform: rotate(0deg);
	position: relative;
	transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
	width: 360px;
	height: 360px;
	margin: 200px auto 0;
}
.cube-wrapper .cube-item{
	position: absolute;
	width: 360px;
	height: 360px;
}
.cube-wrapper .cube-item:first-of-type{
	transform: translateZ(180px);
	background: url('./images/beauty1.jpg') no-repeat center/cover;
}
.cube-wrapper .cube-item:nth-of-type(2){
	transform: rotateY(-90deg) translateZ(180px);
	background: url('./images/beauty2.jpg') no-repeat center/cover;
}
.cube-wrapper .cube-item:nth-of-type(3){
	transform: rotateY(90deg) translateZ(180px);
	background: url('./images/beauty3.jpg') no-repeat center/cover;
}
.cube-wrapper .cube-item:nth-of-type(4){
	transform: rotateX(90deg) translateZ(180px);
	background: url('./images/beauty4.jpg') no-repeat center/cover;
}
.cube-wrapper .cube-item:nth-of-type(5){
	transform: rotateX(-90deg) translateZ(180px);
	background: url('./images/beauty1.jpg') no-repeat center/cover;
}
.cube-wrapper .cube-item:last-of-type{
	transform: rotateY(180deg) translateZ(180px);
	background: url('./images/beauty2.jpg') no-repeat center/cover;
}
.btn{
	position: fixed;
	left: 20px;
	top: 20px;
	padding: 0 10px;
	color: #fff;
	cursor: pointer;
	line-height: 30px;
	font-size: 20px;
	border-radius: 3px;
	box-shadow: 0 0 1px #ccc;
}

背景图片需要自行替换,不然你是看不到图片的,完成了基本的布局大概就是这个样子
cube-album
接下来,我们要做的效果就是如何让它随鼠标拖拽而自由旋转,首先。我们分析下他的旋转行为,大致可以分为水平和竖直方向,基于这两个方向从而让cube自由旋转(任意方向),这就好比高中物理所学的力的合成与分解,力分解图示
问题简化了之后,我们先从单个方向出发,这里以水平方向(X轴),此时cube是绕Y轴旋转的,如下图cube-album
那么如何让cube绕Y轴旋转呢,其实也不难,在我们鼠标按下的时候记录此时的坐标,也就是e.clientX,在鼠标滑滑动的时候用初始值减去滑动时的坐标得到一个差值delta,这个值就是我们想让cube绕Y轴旋转的角度值,在鼠标抬起的时候让鼠标滑动事件变为null从而停止cube旋转,如果你想让cube在鼠标滑动过程中旋转得更快或更慢的话,对差值除以一个比例值即可。效果如下在这里插入图片描述
对于竖直方向的旋转同理,这里就不再赘述了,下面是JS部分

JS部分

const oWrapper = document.getElementsByClassName('wrapper')[0]
const cubeWrapper = document.getElementsByClassName('cube-wrapper')[0]
const btn = document.getElementsByClassName('btn')[0]
let nowyAngle = 0
let ydelta = 0
let nowxAngle = 0
let xdelta = 0
const initX = 0
const initY = 0
function reset () {
	cubeWrapper.style.transitionDuration = '.4s'
	cubeWrapper.style.transform = 'rotateX(' + initX + 'deg)' + 'rotateY(' + initY +'deg)'
	nowxAngle = 0
	nowyAngle = 0
}
oWrapper.onmousedown = function (e) {
	cubeWrapper.style.transitionDuration = '0s'
	e = e || window.event
	e.preventDefault()
	const startY = e.clientX
	const startX = e.clientY
	document.onmousemove = function (e) {
		ydelta = -(startY - e.clientX) / 5 + nowyAngle
		xdelta = (startX - e.clientY) / 5 + nowxAngle
		cubeWrapper.style.transform = 'rotateX(' + xdelta + 'deg)' + 'rotateY(' + ydelta +'deg)'
	}
	document.onmouseup = function () {
		nowyAngle = ydelta
		nowxAngle = xdelta
		this.onmousemove = null
	}
}
btn.onclick = function () {
	reset()
}

其中的比例我设置成了5,大家可以自行更改,也可以用一个变量存储这样修改起来也方便,上文提到的按钮就是重置cube,恢复到初始状态。不过,本文还没有结束哦,对于一个前端工程师来说,当然要考虑用户体验啦~上面虽然达到了我们预期的效果,但是cube旋转并没有惯性旋转,在我们鼠标抬起的时候cube也会立即停止旋转,可以点下面的链接观看demo
cube-album
这效果显然不是最佳的,我们应当给cube加上惯性旋转,就像native app中的惯性滚动一样,这对于用户体验是非常好的,这一点better-scroll就做到了,可以看一下better-scroll源码的动量计算
对于cube水平方向的惯性旋转 = ydelta + distanceY / time,其中distanceY表示鼠标滑动的距离,为了让cube旋转和鼠标滑动方向一致,则 distanceY = startY - e.clientX >= 0?-Math.abs(startY - e.clientX) : Math.abs(startY - e.clientX),在鼠标抬起的时候 nowyAngle = ydelta + distanceY / time
cubeWrapper.style.transitionDuration = ‘1s’ //设置一个过渡时间
cubeWrapper.style.transform = ‘rotateX(’ + nowxAngle + ‘deg)’ + ‘rotateY(’ + nowyAngle +‘deg)’
效果就变成了下图所示,体验就比刚才的好多了~
cube-album
嘿嘿,到这里,我们要做的cube相册就算完成啦~下面是改进后的JS部分和demo演示
cube-album-plus

JS部分

const oWrapper = document.getElementsByClassName('wrapper')[0]
const cubeWrapper = document.getElementsByClassName('cube-wrapper')[0]
const btn = document.getElementsByClassName('btn')[0]
let nowyAngle = 0
let ydelta = 0
let nowxAngle = 0
let xdelta = 0
const initX = 0
const initY = 0
const time = 8
let toggleMove = false
function reset () {
	cubeWrapper.style.transitionDuration = '.4s'
	cubeWrapper.style.transform = 'rotateX(' + initX + 'deg)' + 'rotateY(' + initY +'deg)'
	nowxAngle = 0
	nowyAngle = 0
}
oWrapper.onmousedown = function (e) {
	cubeWrapper.style.transitionDuration = '0s'
	e = e || window.event
	e.preventDefault()
	const startY = e.clientX
	const startX = e.clientY
	let distanceY = 0
	let distanceX = 0
	document.onmousemove = function (e) {
		e.preventDefault()
		toggleMove = true
		distanceY = startY - e.clientX >= 0?-Math.abs(startY - e.clientX) : Math.abs(startY - e.clientX)
		distanceX = startX - e.clientY >= 0?Math.abs(startX - e.clientY) : -Math.abs(startX - e.clientY)
		ydelta = -(startY - e.clientX) / 5 + nowyAngle
		xdelta = (startX - e.clientY) / 5 + nowxAngle
		cubeWrapper.style.transform = 'rotateX(' + xdelta + 'deg)' + 'rotateY(' + ydelta +'deg)'
	}
	document.onmouseup = function (e) {
		e.preventDefault()
		this.onmousemove = null
		if (!toggleMove) return
		nowyAngle = ydelta + distanceY / time
		nowxAngle = xdelta + distanceX / time
		cubeWrapper.style.transitionDuration = '1s'
		cubeWrapper.style.transform = 'rotateX(' + nowxAngle + 'deg)' + 'rotateY(' + nowyAngle +'deg)'
		toggleMove = false
	}
}
btn.onclick = function () {
	reset()
}

四、结语

最后,感谢大家阅读,新年快乐~~

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页