一、前言
最近以来一直在做混合式开发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自由旋转(任意方向),这就好比高中物理所学的力的合成与分解,
问题简化了之后,我们先从单个方向出发,这里以水平方向(X轴),此时cube是绕Y轴旋转的,如下图
那么如何让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相册就算完成啦~下面是改进后的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()
}
四、结语
最后,感谢大家阅读,新年快乐~~