jquery - CSS/Jquery 用鼠标平滑背景移动
问题描述
我试图从缩略图上的鼠标移动中获得平滑平移的缩放图像。
我尝试了所有类型的过渡持续时间、自定义补间函数、jquery animate,但都没有得到好的结果。
当缩略图很小时,跳过它的像素大到足以使图像交错。
jquery动画开始堆叠,css过渡突变,自定义函数,不知道如何轻松改变方向。
const ZOOM_LEVEL = 2;
$(document).ready(function() {
$(".thumb").mouseenter(enter);
$(".thumb").mouseleave(leave);
$('.thumb').mousemove(zoom);
});
function zoom(event) {
const p = calculateZoomOverlay({x: event.pageX, y: event.pageY}, $(event.target));
moveCursorOverlay(p.left, p.top);
movePreviewBackground(p.offsetX, p.offsetY);
}
function calculateZoomOverlay(mouse, thumb) {
let t = thumb.position();
t.width = thumb.width();
t.height = thumb.height();
let z = {}; // Zoom overlay
z.width = t.width / ZOOM_LEVEL;
z.height = t.height / ZOOM_LEVEL;
z.top = mouse.y - z.height / 2;
z.left = mouse.x - z.width / 2;
// Bounce off boundary
if (z.top < t.top) z.top = t.top;
if (z.left < t.left) z.left = t.left;
if (z.top + z.height > t.top + t.height) z.top = t.top + t.height - z.height;
if (z.left + z.width > t.left + t.width) z.left = t.left + t.width - z.width;
z.offsetX = (z.left - t.left) / z.width * 100;
z.offsetY = (z.top - t.top) / z.height * 100;
return z;
}
function moveCursorOverlay(left, top) {
$('.cursor-overlay').css({
top: top,
left: left
});
}
function movePreviewBackground(offsetX, offsetY) {
$('.preview').css({
'background-position': offsetX + '% ' + offsetY + '%'
});
}
function enter() {
// Setup preview image
const imageUrl = $(this).attr('src');
const backgroundWidth = $('.preview').width() * ZOOM_LEVEL;
$('.preview').css({
'background-image': `url(${imageUrl})`,
'background-size': `${backgroundWidth} auto`
});
$('.preview').show();
$('.cursor-overlay').width($(this).width() / ZOOM_LEVEL);
$('.cursor-overlay').height($(this).height() / ZOOM_LEVEL);
$('.cursor-overlay').show();
}
function leave() {
$('.preview').hide();
$('.cursor-overlay').hide();
}
.image-container {
padding: 5px;
display: flex;
flex-direction: row;
}
.thumbnail-container {
display: flex;
flex-direction: column;
}
.thumb {
margin-bottom: 5px;
width: 80px;
height: 50px;
}
.thumb:hover {
-moz-box-shadow: 0 0 5px orange;
-webkit-box-shadow: 0 0 5px orange;
box-shadow: 0 0 5px orange;
}
.preview {
display: none;
margin-left: 15px;
width: 320px;
height: 200px;
border: 3px solid orange;
}
.cursor-overlay {
display: none;
background-color: rgba(0, 150, 50, 0.5);
position: fixed;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="image-container">
<div class="thumbnail-container">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/sbrYaxH.jpg">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/2PpkoRZ.jpg">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/3lOTtJV.jpg">
</div>
<div class="cursor-overlay"></div>
<div class="preview"></div>
</div>
解决方案
在我看来,这是一种 CSS 过渡并不能解决问题的情况。它们通常不适合像这样的交互式动画。许多动画库也无法完成工作。
为了实现这种交互式动画,我喜欢使用以下等式:
loc += (destination - location) / 2
这是一个缓动方程 - 非常适合交互式动画。它可以比作芝诺的二分法悖论
在这里,我们得到了单个轴上的目的地和位置之间的差异,我们将差异减半并将其添加到当前位置。
当我们将此应用于您的代码时,我们最终得到:
z.top += (dy - z.top) / SMOOTH;
z.left += (dx - z.left) / SMOOTH;
为了获得更流畅的动画,我们设置了一个SMOOTH
常数8
。看看它最终的样子:
const ZOOM_LEVEL = 2;
const SMOOTH = 8; // how much smoothness/delay do you want on the animation
let thumb = $('.thumb');
$(document).ready(function() {
thumb.mouseenter(enter);
thumb.mouseleave(leave);
thumb.mousemove(move);
});
let mouseX = 0;
let mouseY = 0;
let z = { top: 0, left: 0 }; // Zoom overlay
let dx;
let dy;
function move(event) {
mouseX = event.pageX;
mouseY = event.pageY;
thumb = $(event.target);
}
function loop() {
const p = calculateZoomOverlay({x: mouseX, y: mouseY});
moveCursorOverlay(p.left, p.top);
movePreviewBackground(p.offsetX, p.offsetY);
window.requestAnimationFrame(loop);
}
loop();
function calculateZoomOverlay(mouse) {
let t = thumb.position();
t.width = thumb.width();
t.height = thumb.height();
z.width = t.width / ZOOM_LEVEL;
z.height = t.height / ZOOM_LEVEL;
dy = mouse.y - z.height / 2;
dx = mouse.x - z.width / 2;
z.top += (dy - z.top) / SMOOTH;
z.left += (dx - z.left) / SMOOTH;
// Bounce off boundary
if (z.top < t.top) z.top = t.top;
if (z.left < t.left) z.left = t.left;
if (z.top + z.height > t.top + t.height) z.top = t.top + t.height - z.height;
if (z.left + z.width > t.left + t.width) z.left = t.left + t.width - z.width;
z.offsetX = (z.left - t.left) / z.width * 100;
z.offsetY = (z.top - t.top) / z.height * 100;
return z;
}
function moveCursorOverlay(left, top) {
$('.cursor-overlay').css({
top: top,
left: left
});
}
function movePreviewBackground(offsetX, offsetY) {
$('.preview').css({
'background-position': offsetX + '% ' + offsetY + '%'
});
}
function enter() {
// Setup preview image
const imageUrl = $(this).attr('src');
const backgroundWidth = $('.preview').width() * ZOOM_LEVEL;
$('.preview').css({
'background-image': `url(${imageUrl})`,
'background-size': `${backgroundWidth} auto`
});
$('.preview').show();
$('.cursor-overlay').width($(this).width() / ZOOM_LEVEL);
$('.cursor-overlay').height($(this).height() / ZOOM_LEVEL);
$('.cursor-overlay').show();
}
function leave() {
$('.preview').hide();
$('.cursor-overlay').hide();
}
.image-container {
padding: 5px;
display: flex;
flex-direction: row;
}
.thumbnail-container {
display: flex;
flex-direction: column;
}
.thumb {
margin-bottom: 5px;
width: 80px;
height: 50px;
}
.thumb:hover {
-moz-box-shadow: 0 0 5px orange;
-webkit-box-shadow: 0 0 5px orange;
box-shadow: 0 0 5px orange;
}
.preview {
display: none;
margin-left: 15px;
width: 320px;
height: 200px;
border: 3px solid orange;
}
.cursor-overlay {
display: none;
background-color: rgba(0, 150, 50, 0.5);
position: fixed;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="image-container">
<div class="thumbnail-container">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/sbrYaxH.jpg">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/2PpkoRZ.jpg">
<img class="thumb" alt="thumbnail" src="https://i.imgur.com/3lOTtJV.jpg">
</div>
<div class="cursor-overlay"></div>
<div class="preview"></div>
</div>
您可以SMOOTH
根据自己的喜好进行调整。
代码结构略有变化。而不是在鼠标移动时执行动画......我们现在使用 arequestAnimationFrame
来不断计算我们的动画并响应变量的变化mouseX/Y
。当用户停止移动鼠标时,这可以防止动画突然停止。
如果z.top
和z.left
值都非常接近或等于dx
和 ,则可以进行优化以结束计算dy
。除非您处理更多计算,否则可能没有必要。
推荐阅读
- python - TypeError: 'pygame.Surface' 对象不可调用(数组有问题,在 Pygame 中)
- cmake - 没有找到所需的“libconfig++”
- rust - 函数中泛型类型的访问字段
- mysql - SUM 和 COUNT 在复杂的 SQL QUERY 中不起作用
- reactjs - 当“jsx”为“react-jsx”时,除非提供“--jsx”标志,否则无法使用 JSX
- python - 部署不和谐机器人时丢失 csv 数据 - python
- swift - 禁用 Crashlytics 调试日志记录
- quarkus - 是否可以使用 quarkus-container-image-docker 仅构建一个给定的图像?
- svg - 单击时 SVG 切换颜色并在单击其他 svg 时返回原始颜色
- mysql - 如何按速度优化mysql的日期组?