Как сделать замену картинок предмета при скролле, что бы создавался эффект вращения предмета
Как можно реализовать эффект, как в этом примере https://takeboost.com/ , что бы по скроллу у элемента менялись изображения и создавался эффект вращения предмета при пролистывании страницы.
У меня получилось реализовать только с учетом условия расстояния от верха страницы и в зависимости от высоты последовательно подставлять нужный ракурс изображения, но это работает, если скроллить медленно, если сделать это быстро, то последовательные изображения не прорисовываются, а появляется только изображение, которое должно быть в этом промежутке высоты.
Может быть есть какая-то библиотека или как это можно реализовать проще всего?
Ниже код с примерной реализацией, знаю, что это можно в цикле, но так нагляднее. Спасибо
const 3dImage = document.querySelector(".3d-image");
const images = [
"/images/1.png",
"/images//2.png",
"/images/3.png"
]
$(window).on("scroll", function () {
var scr = $(this).scrollTop();
if (scr == 0 && scr < 100) {
3dImage .src = images[0];
}
if (scr > 100 && scr < 200) {
3dImage .src = images[1];
}
if (scr > 200 && scr < 300) {
3dImage .src = images[2];
}
}
Ответы (1 шт):
Вариант с canvas и картинками (✔)
За данным вариантом автор вопроса обратился лично, из-за того, что Вариант с картинками подлагивал в FF.
Тут по сути тоже самое, только картинка не грузит DOM, т.к. отрисовывается в canvas.
Так же тут добавлена переменная imgKeyframe = [66, 128], т.к. у автора был набор картинок и нужно было "крутить" определённый "диапазон" картинок (с 66 по 128).
let imgPath = 'https://via.placeholder.com/256.png/999/000/?text=',
img
can = document.querySelector('#rotate-model'),
ctx = can.getContext('2d'),
cW = can.width = window.innerWidth,
cH = can.height = window.innerHeight,
preloadIMG = [],
imgKeyframe = [66, 128], // start, end
imgLen = 0,
imgCur = 0,
step = 3;
window.addEventListener('resize', function(){
cW = can.width = window.innerWidth;
cH = can.height = window.innerHeight;
ctx = can.getContext('2d');
}, false);
function preload() {
for(let i = imgKeyframe[0]; i <= imgKeyframe[1]; i++) {
let img = new Image();
img.src = imgPath+i+'.png';
img.onload = function() {
preloadIMG.push(img);
if(i === imgKeyframe[1]) {
imgLen = preloadIMG.length;
ImgDraw();
RotateScroll();
}
}
}
} preload();
function RotateScroll() {
window.addEventListener('scroll', function(e) {
let a = Math.floor((window.scrollY / imgLen) * step),
i = a >= imgLen ? a - (imgLen * Math.floor(a / imgLen)) : a;
if(imgCur !== i) {
ImgDraw();
imgCur = i;
}
});
}
function ImgDraw() {
let img = preloadIMG[imgCur];
drawImageScaled(img, ctx);
}
function drawImageScaled(img, ctx) {
var canvas = ctx.canvas ;
var hRatio = canvas.width / img.width ;
var vRatio = canvas.height / img.height ;
var ratio = Math.min ( hRatio, vRatio );
var centerShift_x = ( canvas.width - img.width*ratio ) / 2;
var centerShift_y = ( canvas.height - img.height*ratio ) / 2;
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(img, 0,0, img.width, img.height, centerShift_x, centerShift_y, img.width*ratio, img.height*ratio);
}
body {
background-color: transparent;
height: 1000vh;
margin: 0;
}
#rotate-model {
display: block;
width: 100%;
height: 100%;
position: fixed;
left: 0; top: 0; right: 0; bottom: 0;
}
<canvas id="rotate-model"></canvas>
Вариант с 3D моделью и Three.js
Для этого способа, советую произвести оптимизацию модели.
(Обратиться за это к знающим людям, они без проблем это сделают)
Суть такова, модель отрисовывается в canvas и каждый раз когда мы скроллим, она вращается вокруг своей оси, profit.
В качестве примера вращение куба:
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, 1, .1, 1000);
scene.background = null;
const renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setSize(300,300);
document.querySelector('#rotate-model').appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({color: 'blue'});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 2;
function ScrollCubeRotate() {
let scrollY = window.scrollY;
cube.rotation.y = scrollY / 360;
}
ScrollCubeRotate();
function animate() {
requestAnimationFrame(animate);
ScrollCubeRotate();
renderer.render(scene, camera);
}
animate();
body {
height: 1000vh;
margin: 0;
}
#rotate-model {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: fixed;
left: 0; top: 0; right: 0; bottom: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<div id="rotate-model"></div>
Вариант с картинками
Допустим вы отрендерите 360 картинок, с каждым поворотом вашей модели (можно поэкспериментировать и сделать меньше кол-во картинок, чтобы "анимация" поворота не была "рваной" и при этом количество картинок было меньшим) Далее делаем директорию, допустим такую
/rotate-object/
/0.png
/1.png
...
/358.png
/359.png
Далее в переменной imgsLen указываем количество картинок, т.е. imgsLen = 360;
После чего в imgsPath указываем путь до картинок, т.е. imgsLen = '/rotate-object/';
И в функции CreateImages() меняем строку
el.insertAdjacentHTML('beforeend', `<img src="${imgsPath}?text=${i+1}"/>`);
На
el.insertAdjacentHTML('beforeend', `<img src="${imgsPath}${i}.png"/>`);
После этого создадутся картинки и далее заработает скрипт, который при скролле будет заменять картинки, что воссоздаст покадровую анимацию "вращения объекта".
let imgsLen = 30,
imgsPath = 'https://via.placeholder.com/150.png/999/000/',
el = document.querySelector('#rotate-model'),
imgs = false, imgsCur = 0, step = 1;
function CreatImages() {
for(let i = 0; i < imgsLen; i++) {
el.insertAdjacentHTML('beforeend', `<img src="${imgsPath}?text=${i+1}"/>`);
}
imgs = el.querySelectorAll('img');
RotateScroll();
};
CreatImages();
function RotateScroll() {
imgs[imgsCur].style.display = 'block';
window.addEventListener('scroll', function(e) {
let a = Math.floor((window.scrollY / imgsLen) * step),
i = a >= imgsLen ? a - (imgsLen * Math.floor(a / imgsLen)) : a;
if(imgsCur !== i) {
imgs[imgsCur].style.display = '';
imgs[i].style.display = 'block';
imgsCur = i;
}
});
}
body {
height: 1000vh;
margin: 0;
}
#rotate-model {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
position: fixed;
left: 0; top: 0; right: 0; bottom: 0;
}
#rotate-model > img {
display: none;
max-width: 100%;
max-height: 100%;
position: absolute;
}
<div id="rotate-model"></div>
Добавил ещё переменную step, для того, чтобы задавать "ускорение" анимации, в случае когда будет 360 картинок, её потребуется увеличить, ибо за высоту примера, модель не "перевернётся" вокруг своей оси.