使用perspective和transform实现容器上的悬浮效果
2018-03-18 00:00
399 查看
特别声明:此篇文章内容来源于@MIHAI IONESCU的《Animate a Container on Mouse Over Using Perspective and Transform》一文。我一直在做一个网站,这个网站主要用来向用户展示大型图片。我没有创建一个典型的Lightbox效果,而是尝试让一些交互效果更有意思。最后的效果是我在一个容器上添加了一个效果,当用户把鼠标悬浮在图片(用户鼠标移动图片上)时,它会倾斜(鼠标移动到不同的角度时,向不同的方向倾斜)。最终的效果如下:
这个效果是通过CSS和JavaScript实现的。我想通过这个教程来解释每个部分是如何工作的,这样你就可以很容易地复杂或者扩展它。在开始之前,我建议你阅读一下
<div id="inner"></div>
</div>为了演示的目的,把卡片放在屏幕的正中:body {
width: 100%;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
background-color: rgb(220, 220, 220);
}
#container {
perspective: 40px;
}
#inner {
width: 20em;
height: 18em;
background-color: white;
}我们创建了一张白色的卡片,它位于屏幕(浅灰色背景)的正中(水平垂直居中于屏幕)。注意,我们给
var container = document.getElementById('container'),
inner = document.getElementById('inner');
})()
inner = document.getElementById('inner');
var onMouseEnterHandler = function (event) { update(event);}
var onMouseLeaveHandler = function () { inner.style = '';}
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) { update(event);
}
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMoveHandler;这些事情是我们要做或者将要做的事情:事件功能(Handler Functions):这些函数是在事件发生时要处理的事件。我们想要决定当鼠标进入、移动和离开容器时会发生什么,因此每个容器都有一个处理函数
更新功能:我们现在还没有给这个函数添加任何代码,但是它的目的是更新
更新函数的时间:这是另一个还没有编码的函数,但是当需要更新时它会返回
事件(Event):这是一个JavaScript对象,描述发生的事件
上面的代码做的事情是:鼠标移入容器
鼠标在容器
鼠标离开容器
var updateRate = 10;
var isTimeToUpdate = function () {
return counter++ % updateRate === 0;
}当计数器(
var container = document.getElementById('container'),
inner = document.getElementById('inner');
// 鼠标对象
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')'
}
}
mouse.setOrigin(container);让我们一起来讨论这个问题。
最后一行代码
var container = document.getElementById('container'),
inner = document.getElementById('inner'),
helper = document.getElementById('helper');
console.log(container);
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')';
}
}
mouse.setOrigin(container);
var counter = 0;
var updateRate = 1;
var isTimeToUpdate = function () {
return counter ++ % updateRate === 0;
};
var onMouseEnterHandler = function (event) {
helper.className = '';
update(event);
}
var onMouseLeaveHandler = function () {
inner.style = '';
helper.className = 'hidden';
}
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
update(event);
}
displayMousePositionHelper(event);
}
var update = function (event) {
mouse.updatePosition(event);
}
var displayMousePositionHelper = function (event) {
var e = event || window.event;
helper.innerHTML = mouse.show();
helper.style = 'top:' + (e.clientY - container.offsetTop) + 'px;left:' + (e.clientX - container.offsetLeft) + 'px;';
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMoveHandler;
})();
我样想要的是,当你把鼠标移动离容器中心点更远的地方时,那么
mouse.updatePosition (event);
updateTransformStyle(
(mouse.y / inner.offsetHeight / 2).toFixed(2),
(mouse.x / inner.offsetWidth / 2).toFixed(2)
)
}
var updateTransformStyle = function (x, y) {
var style = 'rotateX(' + x + 'deg) rotateY(' + y + 'deg)';
inner.style.transform = style;
inner.style.webkitTransform = style;
}
var container = document.getElementById('container'),
inner = document.ElementBt.Id('inner');
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')';
}
};
mouse.setOrigin(container);
var counter = 0;
var updateRate = 10;
var isTimeToUpdate = function () {
return counter++ % updateRate === 0;
};
var onMouseEnterHandler = function (event) {
update(event);
};
var onMouseLeaveHandler = function () {
inner.style = '';
};
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
update(event);
}
};
var update = function (event) {
mouse.updatePosition(event);
updateTransformStyle(
(mouse.y / inner.offsetHeight / 2).toFixed(2),
(mouse.x / inner.offsetWidth / 2).toFixed(2)
);
};
var updateTransformStyle = function (x, y) {
var style = 'rotateX(' + x + 'deg) rotateY(' + y + 'deg)';
inner.style.transform = style;
inner.style.webkitTransform = style;
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMouveHandler;
})();
每次计数器(添加
#inner { transition: transform 0.5s;}这些都是任意数字。你可以使用
注意,调整页面大小会导致一些问题,因为页面中容器的位置发生了变化。解决方案是在页面大小调整后将鼠标对象(
--mouseX: 0deg;
--mouseY: 0deg;
}和前面示例一样,在
transform: rotateX(calc(var(--mouseY))) rotateY(calc(var(--mouseX)));
transition-origin: center;
transition: transform 0.5s;
box-shadow: 2px 2px 50px rgba(0, 0, 0, 0.2);}添加对应的几个JavaScript对应的鼠标事件,相比之下,这个代码变得更简单多了,代码量也少了:(function(){
var container = document.getElementById('container'),
inner = document.getElementById('inner');
var counter = 0;
var refreshRate = 10;
var isTimeToUpdate = function() {
return counter++ % refreshRate === 0;
};
var updateTransformStyle = function (xPos, yPos) {
var wh = window.innerHeight / 2,
ww = window.innerWidth / 2;
inner.style.setProperty('--mouseX', (xPos - ww) / 300 + 'deg');
inner.style.setProperty('--mouseY', (yPos - wh) / 300 + 'deg');
}
var onMouseEnterHandler = function(event) {
updateTransformStyle(event.clientX,event.clientY);
};
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
updateTransformStyle(event.clientX,event.clientY);
}
}
var onMouseLeaveHandler = function() {
inner.style = "";
};
var onTouchMoveHander = function (event) {
event.preventDefault();
var touch = event.targetTouches[0];
if (touch) {
updateTransformStyle(touch.pageX, touch.pageY);
}
}
container.onmousemove = onMouseMoveHandler;
container.ontouchmove = onTouchMoveHander;
container.onmouseleave = onMouseLeaveHandler;
})()最终的效果如下:
文章涉及到图片和代码,如果展示不全给您带来不好的阅读体验,欢迎点击文章底部的 阅读全文。如果您觉得小站的内容对您的工作或学习有所帮助,欢迎关注此公众号。
W3cplus.com————————————记述前端那些事,引领web前沿
长按二维码,关注W3cplus▼
这个效果是通过CSS和JavaScript实现的。我想通过这个教程来解释每个部分是如何工作的,这样你就可以很容易地复杂或者扩展它。在开始之前,我建议你阅读一下
perspective和
transfrom相关的教程。在这篇文章中介绍的效果将会使用这些属性,熟悉它们有助于你更好的理解下面的内容。那我们开始今天的教程学习吧。
设置
首先我们需要在一个容器中放置一个元素。这个容器将有助于我们使用perspective属性。<div id="container">
<div id="inner"></div>
</div>为了演示的目的,把卡片放在屏幕的正中:body {
width: 100%;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
background-color: rgb(220, 220, 220);
}
#container {
perspective: 40px;
}
#inner {
width: 20em;
height: 18em;
background-color: white;
}我们创建了一张白色的卡片,它位于屏幕(浅灰色背景)的正中(水平垂直居中于屏幕)。注意,我们给
#container设置了
perspective的值为
40px。在这一点上我们什么也没有做,因为我们没有创建任何
transform。这将在稍后的JavaScript中处理。(function(){ // 初始化
var container = document.getElementById('container'),
inner = document.getElementById('inner');
})()
开始添加JavaScript脚本
下面将是我们需要的一些功能:var container = document.getElementById('container'),inner = document.getElementById('inner');
var onMouseEnterHandler = function (event) { update(event);}
var onMouseLeaveHandler = function () { inner.style = '';}
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) { update(event);
}
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMoveHandler;这些事情是我们要做或者将要做的事情:事件功能(Handler Functions):这些函数是在事件发生时要处理的事件。我们想要决定当鼠标进入、移动和离开容器时会发生什么,因此每个容器都有一个处理函数
更新功能:我们现在还没有给这个函数添加任何代码,但是它的目的是更新
#inner元素的有关于3D旋转相关的样式
更新函数的时间:这是另一个还没有编码的函数,但是当需要更新时它会返回
true。这是一种方法,可以减少对
update()函数的调用次数,并提高脚本的性能
事件(Event):这是一个JavaScript对象,描述发生的事件
上面的代码做的事情是:鼠标移入容器
#container时,更新
div#inner的3D旋转属性
鼠标在容器
#container移动时,更新内部
div#inner的3D旋转属性
鼠标离开容器
#container时,重置
div#inner的样式
是时候更新了吗?
让我们添加决定何时更新div#inner的3D旋转。var counter = 0;
var updateRate = 10;
var isTimeToUpdate = function () {
return counter++ % updateRate === 0;
}当计数器(
counter)到达
updateRate时,将进行更新。在这一点上,你可以尝试使用
console.log()来替换
update函数,并使用
updateRate来查看它是如何一起工作的。
鼠标
接下来是鼠标对象。这个比其他部分要更复杂一些。尽管如此,理解起来并不是那么困难,但是代码看起来很吓人,特别是如果你是JavaScript新手的话。// 初始化var container = document.getElementById('container'),
inner = document.getElementById('inner');
// 鼠标对象
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')'
}
}
mouse.setOrigin(container);让我们一起来讨论这个问题。
show():显示鼠标的当前位置
setOrigin(e):在
element(e)的中心设置鼠标对象的坐标
coordinates(0,0)
updatePosition():相对于
(0,0)更新鼠标对象的当前位置
最后一行代码
mouse.setOrigin(container)将鼠标对象的
coordinates(0,0)设置在容器的中心。这里有一个例子说明了这一点。(function(){
var container = document.getElementById('container'),
inner = document.getElementById('inner'),
helper = document.getElementById('helper');
console.log(container);
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')';
}
}
mouse.setOrigin(container);
var counter = 0;
var updateRate = 1;
var isTimeToUpdate = function () {
return counter ++ % updateRate === 0;
};
var onMouseEnterHandler = function (event) {
helper.className = '';
update(event);
}
var onMouseLeaveHandler = function () {
inner.style = '';
helper.className = 'hidden';
}
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
update(event);
}
displayMousePositionHelper(event);
}
var update = function (event) {
mouse.updatePosition(event);
}
var displayMousePositionHelper = function (event) {
var e = event || window.event;
helper.innerHTML = mouse.show();
helper.style = 'top:' + (e.clientY - container.offsetTop) + 'px;left:' + (e.clientX - container.offsetLeft) + 'px;';
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMoveHandler;
})();
我样想要的是,当你把鼠标移动离容器中心点更远的地方时,那么
div#inner旋转的幅度也将更大。
更新鼠标位置的样式
这是update()函数里的代码:var update = function (event) {
mouse.updatePosition (event);
updateTransformStyle(
(mouse.y / inner.offsetHeight / 2).toFixed(2),
(mouse.x / inner.offsetWidth / 2).toFixed(2)
)
}
var updateTransformStyle = function (x, y) {
var style = 'rotateX(' + x + 'deg) rotateY(' + y + 'deg)';
inner.style.transform = style;
inner.style.webkitTransform = style;
}
update():更新鼠标位置和更新
div#inner的样式
updateTransformStyle():更新每个供应商前缀的样式
我们做了什么?
我们最好做些测试!看起来,当鼠标进入并退出卡片时,我们的perspective会发生变化,但它并不像我们想象的那么平滑。(function(){
var container = document.getElementById('container'),
inner = document.ElementBt.Id('inner');
var mouse = {
_x: 0,
_y: 0,
x: 0,
y: 0,
updatePosition: function (event) {
var e = event || window.event;
this.x = e.clientX - this._x;
this.y = (e.clientY - this._y) * -1;
},
setOrigin: function (e) {
this._x = e.offsetLeft + Math.floor(e.offsetWidth / 2);
this._y = e.offsetTop + Math.floor(e.offsetHeight / 2);
},
show: function () {
return '(' + this.x + ',' + this.y + ')';
}
};
mouse.setOrigin(container);
var counter = 0;
var updateRate = 10;
var isTimeToUpdate = function () {
return counter++ % updateRate === 0;
};
var onMouseEnterHandler = function (event) {
update(event);
};
var onMouseLeaveHandler = function () {
inner.style = '';
};
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
update(event);
}
};
var update = function (event) {
mouse.updatePosition(event);
updateTransformStyle(
(mouse.y / inner.offsetHeight / 2).toFixed(2),
(mouse.x / inner.offsetWidth / 2).toFixed(2)
);
};
var updateTransformStyle = function (x, y) {
var style = 'rotateX(' + x + 'deg) rotateY(' + y + 'deg)';
inner.style.transform = style;
inner.style.webkitTransform = style;
}
container.onmouseenter = onMouseEnterHandler;
container.onmouseleave = onMouseLeaveHandler;
container.onmousemove = onMouseMouveHandler;
})();
每次计数器(
counter)刷新
updateRate时更新
div#inner的旋转值。这在更新之间产生了迟钝的
transform效果。如何破这个局呢?可以使用CSS的
transition。
添加transition
效果
#inner { transition: transform 0.5s;}这些都是任意数字。你可以使用perspective和
transform的值,使效果或多或少地达到你认为合适的效果。
注意,调整页面大小会导致一些问题,因为页面中容器的位置发生了变化。解决方案是在页面大小调整后将鼠标对象(
mouse)重新放到容器(
container)中。
CSS 自定义属性
到整理这文章之时,CSS的自定义属性(CSS变量)得到了众多浏览器的支持,对于文章开头演示的效果,我们可以尝试使用CSS的自定义属性来实现,这将会让事情变得更为简单。这里来看一个简单的示例。使用CSS自定义变量,需要先声明相关的CSS变量,这里我们需要控制的是鼠标x和
y的坐标,所以我们先在
:root中声明对应的两个变量名,并且设置其默认值为
0::root {
--mouseX: 0deg;
--mouseY: 0deg;
}和前面示例一样,在
#container容器中设置
perspective的值,这个值根据自己想要的效果可以进行调整,另外在这个容器中使用
perspective-origin设置
perspective的中心点为容器的中心。与前面示例不同的是,
#inner是的
transform是通过JavaScript对应的鼠标事件来控制,而我们的示例中在样式中调用CSS的自定义变量,然后再通过JavaScript的
.setProperty()来控制调用的变量值。先来看CSS部分的代码:#inner {
transform: rotateX(calc(var(--mouseY))) rotateY(calc(var(--mouseX)));
transition-origin: center;
transition: transform 0.5s;
box-shadow: 2px 2px 50px rgba(0, 0, 0, 0.2);}添加对应的几个JavaScript对应的鼠标事件,相比之下,这个代码变得更简单多了,代码量也少了:(function(){
var container = document.getElementById('container'),
inner = document.getElementById('inner');
var counter = 0;
var refreshRate = 10;
var isTimeToUpdate = function() {
return counter++ % refreshRate === 0;
};
var updateTransformStyle = function (xPos, yPos) {
var wh = window.innerHeight / 2,
ww = window.innerWidth / 2;
inner.style.setProperty('--mouseX', (xPos - ww) / 300 + 'deg');
inner.style.setProperty('--mouseY', (yPos - wh) / 300 + 'deg');
}
var onMouseEnterHandler = function(event) {
updateTransformStyle(event.clientX,event.clientY);
};
var onMouseMoveHandler = function (event) {
if (isTimeToUpdate()) {
updateTransformStyle(event.clientX,event.clientY);
}
}
var onMouseLeaveHandler = function() {
inner.style = "";
};
var onTouchMoveHander = function (event) {
event.preventDefault();
var touch = event.targetTouches[0];
if (touch) {
updateTransformStyle(touch.pageX, touch.pageY);
}
}
container.onmousemove = onMouseMoveHandler;
container.ontouchmove = onTouchMoveHander;
container.onmouseleave = onMouseLeaveHandler;
})()最终的效果如下:
总结
现在我们完成了想要的效果。我们有了一个容器来让元素变得更具交互性。本文开头演示的效果中加入了图片,但是除了图像之外,它还可以用于其他任何你想要的东西,包括表单,Modal弹出框或者你想要的其他任何内容。感兴的同学,不仿试试!文章涉及到图片和代码,如果展示不全给您带来不好的阅读体验,欢迎点击文章底部的 阅读全文。如果您觉得小站的内容对您的工作或学习有所帮助,欢迎关注此公众号。
W3cplus.com————————————记述前端那些事,引领web前沿
长按二维码,关注W3cplus▼
相关文章推荐
- [OpenCv]使用getPerspectiveTransform()函数实现Perspective Effect(透视效果)
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上)
- (30):Silverlight 2 使用Transform实现更炫的效果(下)
- (29):Silverlight 2 使用Transform实现更炫的效果(上)
- 一步一步学Silverlight 2系列(30):使用Transform实现更炫的效果(下) (转)
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上)
- 一步一步学Silverlight 2系列(30):使用Transform实现更炫的效果(下)
- 2011年最新使用CSS3实现各种独特悬浮效果的教程
- 分享使用jQuery和CSS实现的一个超酷缩略图悬浮逼近效果
- 第29课 使用Transform实现更炫的效果(上)
- 分享使用jQuery和CSS实现的一个超酷缩略图悬浮逼近效果
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上)
- 使用jQuery实现鼠标悬浮图片轮换效果
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上) (转)
- 一步一步学Silverlight 2系列(30):使用Transform实现更炫的效果(下)
- 使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果
- 使用jQuery实现鼠标悬浮图片轮换效果
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上)
- 一步一步学Silverlight 2系列(29):使用Transform实现更炫的效果(上)
- 一步一步学Silverlight 2系列(30):使用Transform实现更炫的效果(下)