如何以编程方式有效地平移整个图像?
Posted
技术标签:
【中文标题】如何以编程方式有效地平移整个图像?【英文标题】:How can I effectively pan an entire image programmatically? 【发布时间】:2022-01-20 09:28:51 【问题描述】:我有一个 11500x11500 的 div,它包含 400 张图像,显然溢出了视口。
我想以编程方式平移 整个 div。
我想生成一个动画,当动画结束时,整个 div 必须已经在视口中从上到下、从左到右平移。
现在,我正在将我的 11500x1500 div “拆分”为 tiles。每个图块的最大宽度和高度就是视口的宽度和高度。
我存储每个图块的坐标,然后随机选择一个,从左到右平移,然后移动到下一个。
我想知道:
-
我的方法是否正确,或者我的计算/方法中是否遗漏了一些东西,并且可以改进。考虑到尺寸,我很难判断我是否真的在平移整个 div
能否让平移效果感觉更“有机”/“自然”。为了确保整个 div 最终被平移,我选择每个图块并从左到右平移它,然后移动到下一个等等。这感觉有点僵硬和过于形式化。有没有办法以一个角度或一个更随机的运动进行平移,但要确保整个 div 最终会被平移?
提前感谢您的帮助。
这是jsfiddle,这是代码(为了示例/测试,每个“图像”实际上是一个包含其索引为文本的 div):
function forMs(time)
return new Promise((resolve) =>
setTimeout(() =>
resolve()
, time)
)
let container = document.getElementById('container')
let
width,
height
= container.getBoundingClientRect()
let minLeft = window.innerWidth - width
let minTop = window.innerHeight - height
let i = 0
while (i < 400)
// adding "image" to the container
let image = document.createElement('div')
// add some text to the "image"
// to know what we're looking at while panning
image.innerhtml = ''
let j = 0
while (j < 100)
image.innerHTML += ` $i + 1`
j++
container.appendChild(image)
i++
let coords = []
let x = 0
while (x < width)
let y = 0
while (y < height)
coords.push(
x,
y
)
y += window.innerHeight
x += window.innerWidth
async function pan()
if (!coords.length)
return;
let randomIdx = Math.floor(Math.random() * coords.length)
let [randomCoord] = coords.splice(randomIdx, 1);
console.log(coords.length)
container.classList.add('fast')
// update style in new thread so new transition-duration is applied
await forMs(10)
// move to new yet-unpanned area
container.style.top = Math.max(-randomCoord.y, minTop) + 'px'
container.style.left = Math.max(-randomCoord.x, minLeft) + 'px'
// wait (approx.) for transition to end
await forMs(2500)
container.classList.remove('fast')
// update style in new thread so new transition-duration is applied
await forMs(10)
//pan that area
let newLeft = -(randomCoord.x + window.innerWidth)
if (newLeft < minLeft)
newLeft = minLeft
container.style.left = newLeft + 'px'
// wait (approx.) for transition to end
await forMs(4500)
// move on to next random area
await pan()
pan()
html,
body
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: auto;
*
margin: 0;
padding: 0;
#container
position: absolute;
top: 0;
left: 0;
text-align: left;
width: 11500px;
height: 11500px;
transition: all 4s ease-in-out;
transition-property: top left;
font-size: 0;
#container.fast
transition-duration: 2s;
#container div
display: inline-block;
height: 575px;
width: 575px;
border: 1px solid black;
box-sizing: border-box;
font-size: 45px;
overflow: hidden;
word-break: break-all;
<div id="container"></div>
【问题讨论】:
【参考方案1】:我认为可以进行以下改进:
隐藏 html 和 body 上的溢出,这样用户就不能移动滚动条并干扰流程。 每次都计算minLeft
和minTop
以考虑调整窗口大小。您可能需要ResizeObserver 来重新计算。
增加转换时间以避免Cybersickness。在更坏的情况下,RNG 将首先选择右下角的瓷砖,这样你的容器将在 2 秒内移动最长!也许,您可以缩小并移动然后放大然后执行平移。或者使用任何serpentine 路径,这将使跳转更短。
性能改进:
使用变换而不是顶部,左侧用于动画。 使用 will-change: transform;
。 will-change 会让浏览器知道要优化什么。
使用translate3D()
而不是translate()
。 ref
使用requestAnimationFrame。避免使用 setTimeout、setInterval。
这是一篇旧文但很好的文章:https://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
修改代码以使用变换:
function forMs(time)
return new Promise((resolve) =>
setTimeout(() =>
resolve()
, time)
)
let container = document.getElementById('container')
let stat = document.getElementById('stats');
let
width,
height
= container.getBoundingClientRect()
let minLeft = window.innerWidth - width
let minTop = window.innerHeight - height
let i = 0
while (i < 400)
// adding "image" to the container
let image = document.createElement('div')
// add some text to the "image"
// to know what we're looking at while panning
image.innerHTML = ''
let j = 0
while (j < 100)
image.innerHTML += ` $i + 1`
j++
container.appendChild(image)
i++
let coords = []
let x = 0
while (x < width)
let y = 0
while (y < height)
coords.push(
x,
y
)
y += window.innerHeight
x += window.innerWidth
let count = 0;
async function pan()
if (!coords.length)
stat.innerText = 'iteration: ' +
(++count) + '\n tile# ' + randomIdx + ' done!!';
stat.style.backgroundColor = 'red';
return;
let minLeft = window.innerWidth - width
let minTop = window.innerHeight - height
let randomIdx = Math.floor(Math.random() * coords.length);
randomIdx = 1; //remove after debugging
let [randomCoord] = coords.splice(randomIdx, 1);
stat.innerText = 'iteration: ' +
(++count) + '\n tile# ' + randomIdx;
console.log(coords.length + ' - ' + randomIdx)
container.classList.add('fast')
// update style in new thread so new transition-duration is applied
await forMs(10)
// move to new yet-unpanned area
let yy = Math.max(-randomCoord.y, minTop);
let xx = Math.max(-randomCoord.x, minLeft);
move(xx, yy);
// wait (approx.) for transition to end
await forMs(2500)
container.classList.remove('fast')
// update style in new thread so new transition-duration is applied
await forMs(10)
//pan that area
let newLeft = -(randomCoord.x + window.innerWidth)
if (newLeft < minLeft)
newLeft = minLeft
xx = newLeft;
//container.style.left = newLeft + 'px'
move(xx, yy);
// wait (approx.) for transition to end
await forMs(4500)
// move on to next random area
await pan()
pan()
function move(xx, yy)
container.style.transform = "translate3D(" + xx + "px," + yy + "px,0px)";
html,
body
position: relative;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
*
margin: 0;
padding: 0;
box-sizing: border-box;
#container
text-align: left;
width: 11500px;
height: 11500px;
transition: all 4s ease-in-out;
transition-property: transform;
font-size: 0;
will-change: transform;
#container.fast
transition-duration: 2s;
#container div
display: inline-block;
height: 575px;
width: 575px;
border: 1px solid black;
box-sizing: border-box;
font-size: 45px;
overflow: hidden;
word-break: break-all;
#stats
border: 2px solid green;
width: 100px;
background-color: lightgreen;
position: fixed;
opacity: 1;
top: 0;
left: 0;
z-index: 10;
<div id=stats>iteration: 1 tile# 11</div>
<div id="container"></div>
请注意,我还没有实现上面 sn-p 中的所有内容。
【讨论】:
非常感谢您抽出宝贵时间提供如此详尽的答案。特别是变换建议真的很有帮助,因为改变顶部/左侧确实很颠簸。我会更新并回复你。谢谢! 不幸的是,使用 CSS 转换的代价是在转换发生时很多图像没有被渲染。并且它们仅在过渡结束时呈现。而在我最初的实现中,过渡更加颠簸,但所有图像都会不断呈现。 事情发展得太快了。 :( 有先进的技术可以实现higher fps。您需要尝试使用实际图像而不是仅文本,最后这很重要。如果您加载那么多图像,您还需要检查内存要求。对于加载时间,您可以需要使用单个精灵。以上是关于如何以编程方式有效地平移整个图像?的主要内容,如果未能解决你的问题,请参考以下文章