I'm trying to create an scrolling animation with a canvas. The problem I'm running in to is I want to change the "position" from sticky to something else so that it stays in the div.
Because right now that isn't the case as you can see down here.
Image website
If anyone has an idea please let me know
import React, { useRef, useEffect} from 'react';
import './index.css';
export default function animation(){
const html = document.documentElement;
const currentFrame = index => (
`https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero- lightpass/${index.toString().padStart(4, '0')}.jpg`
)
const frameCount = 145;
const canvasHeight = 770;
const canvasWidth = 1158;
const img = new Image();
img.src = currentFrame(1);
const Canvas = props => {
const canvasRef = useRef(null)
useEffect(() => {
const canvas = canvasRef.current
const context = canvas.getContext('2d')
img.onload = function(){
context.drawImage(img,0,0)
}
const updateImage = index =>{
img.src = currentFrame(index)
context.drawImage(img,0,0)
}
window.addEventListener('scroll', ()=>{
const scrollTop = html.scrollTop;
const maxScrollTop = html.scrollHeight - window.innerHeight;
const scrollFraction = scrollTop/maxScrollTop;
const frameIndex = Math.min(frameCount-1, Math.floor(scrollFraction * frameCount))
requestAnimationFrame(()=> updateImage(frameIndex + 1))
})
}, [])
return <canvas ref={canvasRef} {...props}/>
}
return(
<Canvas className="animation" width={canvasWidth} height={canvasHeight}></Canvas>
)
}
html{
height: 100vh;
}
body{
height: 400vh;
}
canvas{
position: -webkit-sticky;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
max-height: 770px;
max-width: 1158px;
background: rgb(0, 0, 0);
overflow-x: hidden;
}
Set div element position relative.
position: relative;
Related
But When i select the other picture it did not show in magnifier box, instead it show the default picture in magnifier. how i can fix that?. I want to change the image after selecting from below and magnifier should work on that image.
and magnifier position is very downside, can we also make the appropriate position
HTML
Css
.product-image {
height: 300px;
cursor: zoom-in;
}
.magnifier-container {
display: inline-block;
position: relative;
}
.magnifier {
display: none;
position: absolute;
top: 0;
left: 100%;
overflow: hidden;
height: 300px;
width: 300px;
border: 1px solid #ddd;
border-radius: 10px;
background-color: white;
}
.magnifier__img {
width: 1000px;
transform-origin: 150px 150px;
}
js
// most efficient way to add HTML, faster than innerHTML
const parseHTML = (htmlStr) => {
const range = document.createRange();
range.selectNode(document.body); // required in Safari
return range.createContextualFragment(htmlStr);
};
// pass this function any image element to add magnifying functionality
const makeImgMagnifiable = (img) => {
const magnifierFragment = parseHTML(`
<div class="magnifier-container">
<div class="magnifier">
<img class="magnifier__img" src="${img.src}"/>
</div>
</div>
`);
// This preserves the original element reference instead of cloning it.
img.parentElement.insertBefore(magnifierFragment, img);
const magnifierContainerEl = document.querySelector(".magnifier-container");
img.remove();
magnifierContainerEl.appendChild(img);
// query the DOM for the newly added elements
const magnifierEl = magnifierContainerEl.querySelector(".magnifier");
const magnifierImg = magnifierEl.querySelector(".magnifier__img");
// set up the transform object to be mutated as mouse events occur
const transform = {
translate: [0, 0],
scale: 1,
};
// shortcut function to set the transform css property
const setTransformStyle = (el, { translate, scale }) => {
const [xPercent, yRawPercent] = translate;
const yPercent = yRawPercent < 0 ? 0 : yRawPercent;
// make manual pixel adjustments to better center
// the magnified area over the cursor.
const [xOffset, yOffset] = [
`calc(-${xPercent}% + 250px)`,
`calc(-${yPercent}% + 70px)`,
];
el.style = `
transform: scale(${scale}) translate(${xOffset}, ${yOffset});
`;
};
// show magnified thumbnail on hover
img.addEventListener("mousemove", (event) => {
const [mouseX, mouseY] = [event.pageX + 40, event.pageY - 20];
const { top, left, bottom, right } = img.getBoundingClientRect();
transform.translate = [
((mouseX - left) / right) * 100,
((mouseY - top) / bottom) * 100,
];
magnifierEl.style = `
display: block;
top: ${mouseY}px;
left: ${mouseX}px;
`;
setTransformStyle(magnifierImg, transform);
});
// zoom in/out with mouse wheel
img.addEventListener("wheel", (event) => {
event.preventDefault();
const scrollingUp = event.deltaY < 0;
const { scale } = transform;
transform.scale =
scrollingUp && scale < 3
? scale + 0.1
: !scrollingUp && scale > 1
? scale - 0.1
: scale;
setTransformStyle(magnifierImg, transform);
});
// reset after mouse leaves
img.addEventListener("mouseleave", () => {
magnifierEl.style = "";
magnifierImg.style = "";
});
};
const img = document.querySelector(".product-image");
makeImgMagnifiable(img);
I want to implement a zoom effect on certain areas of the image on hover. I tried to do it but can't think of a correct solution.
let's say I can't enlarge the top right corner.
window.addEventListener('DOMContentLoaded', () => {
const wrapper = document.querySelector('.wrapper');
const img = document.querySelector('img');
const handleMouseMove = (e) => {
const { left, top, width, height } = e.target.getBoundingClientRect();
const x = ((e.pageX - left) / width) * 100;
const y = ((e.pageY - top) / height) * 100;
const styles = `transform: scale(2); transform-origin: ${x}px ${y}px`;
img.style = styles;
}
const handleMouseLeave = () => {
img.style = '';
console.log('leave');
}
wrapper.addEventListener('mousemove', (e) => handleMouseMove(e));
wrapper.addEventListener('mouseleave', handleMouseLeave);
});
.wrapper {
width: 300px;
height: 300px;
margin: 50px auto;
overflow: hidden;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="wrapper">
<img src="https://img.freepik.com/free-photo/adorable-brown-white-basenji-dog-smiling-giving-high-five-isolated-white_346278-1657.jpg?size=626&ext=jpg&ga=GA1.2.1449299337.1618704000" alt="">
</div>
I could understand your objective. The principal error was that you had been trying to update the zoom in basis of the current position of the image, which was constantly changing because you were changing the image properties.
In my solution I used the left, and top variables to make it the most generic possible, it could be easier in React.js in my opinion.
I take the top and left position on the first load, that the real solution.
window.addEventListener('DOMContentLoaded', () => {
const wrapper = document.querySelector('.wrapper');
const img = document.querySelector('img');
const { left, top } = wrapper.getBoundingClientRect();
const handleMouseMove = (e) => {
const x = (e.pageX - left);
const y = (e.pageY - top);
const styles = `transform: scale(2); transform-origin: ${x}px ${y}px`;
img.style = styles;
}
const handleMouseLeave = () => {
img.style = '';
}
wrapper.addEventListener('mousemove', (e) => handleMouseMove(e));
wrapper.addEventListener('mouseleave', handleMouseLeave);
});
.wrapper {
width: 300px;
height: 300px;
margin: 50px auto;
overflow: hidden;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
<div class="wrapper">
<img src="https://img.freepik.com/free-photo/adorable-brown-white-basenji-dog-smiling-giving-high-five-isolated-white_346278-1657.jpg?size=626&ext=jpg&ga=GA1.2.1449299337.1618704000" alt="">
</div>
You made the calculations way more complicated than you had to.
I changed ...
const x = ((e.pageX - left) / width) * 100;
const y = ((e.pageY - top) / height) * 100;
... to ...
const x = e.pageX - left;
const y = e.pageY - top;
but the real magic happens when I added pointer-events: none; to img so it's always sending in .wrapper as e.target.
NOTE: the zoom functionality bugs out if the image is "over" the edge of the page, i.e. if you need to scroll down to see the full image. This happens in the original code as well. Just a heads up.
window.addEventListener('DOMContentLoaded', () => {
const wrapper = document.querySelector('.wrapper');
const img = document.querySelector('img');
const handleMouseMove = (e) => {
const { left, top, width, height } = e.target.getBoundingClientRect();
const x = e.pageX - left;
const y = e.pageY - top;
const styles = `transform: scale(2); transform-origin: ${x}px ${y}px`;
img.style = styles;
}
const handleMouseLeave = () => {
img.style = '';
}
wrapper.addEventListener('mousemove', (e) => handleMouseMove(e));
wrapper.addEventListener('mouseleave', handleMouseLeave);
});
.wrapper {
width: 300px;
height: 300px;
margin: 50px auto;
overflow: hidden;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
pointer-events: none;
}
<div class="wrapper">
<img src="https://img.freepik.com/free-photo/adorable-brown-white-basenji-dog-smiling-giving-high-five-isolated-white_346278-1657.jpg?size=626&ext=jpg&ga=GA1.2.1449299337.1618704000" alt="">
</div>
I have been having a problem with a drawing on a canvas. I want the user to be able to change the background on the canvas and I will eventually want to be able to type something in an input box and it will appear on the canvas in boxes. I have been trying to get more on the screen after they have choosen a background (which works fine), but when I try to add just a simple box I can't. I have been trying to do it in different parts of the code but doesn't work for me, and haven't been able to find a solution.
So what I am asking, is there a specific way you have to do it in order to have
a image (choosen with a select tag, which I already have working), and be able to draw a box ontop of the image choosen. Hope someone can explain to me how I will be able to do so!
function load(){
draw()
}
//Canvas change background
function draw(){
changeBackground("assets/images/1.jpg");
}
function changeBackground(imagePath){
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=new Image();
img.onload = function(){
ctx.drawImage(img,0,0,954,507);
};
img.src=imagePath;
}
function background(){
var imageN = document.getElementById("imageselector").value;
console.log("Image picked as a Background: " + imageN)
changeBackground("assets/images/" + imageN + ".jpg");
}
Let this be a good foundation for your app!
Happy coding!
const canvas = document.querySelector("#canvas");
const context = canvas.getContext("2d");
const imageInput = document.querySelector("#imageInput");
const textInput = document.querySelector("#textInput");
const imageUrl = 'https://i.picsum.photos/id/944/600/600.jpg';
imageInput.value = imageUrl;
const state = {
image: null,
text: ''
}
const width = window.innerWidth;
const height = window.innerHeight;;
canvas.width = width;
canvas.height = height;
textInput.addEventListener("keydown", ({key}) => {
const value = textInput.value;
if(key === "Backspace") {
state.text = value.substring(0, value.length - 1);
}
else {
state.text = value + key;
}
render();
})
imageInput.addEventListener("input", () => {
getImage(imageInput.value)
.then(image => {
state.image = image;
render();
})
})
const render = () => {
clear();
drawBackground(state.image);
drawText(state.text);
}
const drawText = (text) => {
context.font = "40px Comic Sans";
context.fillStyle = "white";
const textMeasure = context.measureText(text);
context.fillText(text, width / 2 - textMeasure.width / 2, height / 5);
}
const clear = () => {
context.fillStyle = "white";
context.fillRect(0, 0, width, height);
}
const getImage = (imagePath) => new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve(image);
};
image.src = imagePath;
})
const drawBackground = (image) => {
context.drawImage(image, 0, 0, width, height);
}
getImage(imageInput.value)
.then(image => {
state.image = image;
render();
})
html, body {
margin: 0;
height: 100%;
box-sizing: border-box;
}
#box {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
#wrapper {
background: white;
}
#textInput, #imageInput {
width: 300px;
padding: 1rem;
background: transparent;
border: none;
}
#canvas {
position: absolute;
z-Index: -1;
}
<canvas id="canvas"></canvas>
<div id="box">
<div id="wrapper">
<input id="imageInput" type="text" placeholder="Enter src or dataUri of an image...">
<div>
<input id="textInput" type="text" placeholder="Enter text to show up...">
</div>
</div>
</div>
I just had to delay the text and boxes and then it worked :)
I have an element that I want to appear at the exact spot of another. This other element is transformed in its scale and rotation. The first element must assume the appearance of the other using transforms only.
const getRotation = el => {
const st = window.getComputedStyle(el, null);
const tr = st.getPropertyValue('transform');
const values = tr.split('(')[1].split(')')[0].split(',');
const a = values[0];
const b = values[1];
const angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
return angle;
}
const firstPhoto = document.querySelector('.first');
const secondPhoto = document.querySelector('.second');
const firstPhotoBox = firstPhoto.getBoundingClientRect();
const secondPhotoBox = secondPhoto.getBoundingClientRect();
const deltaX = secondPhotoBox.left - firstPhotoBox.left;
const deltaY = secondPhotoBox.top - firstPhotoBox.top;
const deltaW = secondPhotoBox.width / firstPhotoBox.width;
const deltaH = secondPhotoBox.height / firstPhotoBox.height;
const deltaR = getRotation(secondPhoto);
firstPhoto.style.transform = `translate(${deltaX}px, ${deltaY}px) scaleX(${deltaW}) scaleY(${deltaH}) rotateZ(${deltaR}deg)`;
div {
opacity: 0.8;
}
.first {
width: 200px;
height: 100px;
background: green;
}
.second {
width: 260px;
height: 400px;
background: orange;
transform: translate(20%, 5%) scale(0.8) rotateZ(-2deg);
}
<div class="first">Element 1</div>
<div class="second">Element 2</div>
I'm building a transition where I want to scale up a thumbnail to fill the viewport on click.
Currently I got the calculations right on finding the right scale ratio based on the viewport and it's size, but I'm having problems on centring the image in the viewport after it's scaled/expanded.
I only have the problem when I'm using transform-origin: center center; and the image is centered in a fixed full screen container. Here is a jsfiddle showing my problem.
I've also made a jsfiddle showing how it is without the centering. This is close to what I want, except I want to be able to transition the image from other positions rather than the top left corner.
Here is the code I have so far
// Helper fucntions
const getWindowWidth = () => {
return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
}
const getWindowHeight = () => {
return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
}
// Get element
const El = document.getElementById('test');
// Add listener
El.addEventListener('click', () => {
// Viewport size
const viewH = getWindowHeight();
const viewW = getWindowWidth();
// El size
const elHeight = El.offsetHeight;
const elWidth = El.offsetWidth;
// Scale ratio
let scale = 1;
// Get how much element need to scale
// to fill viewport
// Based on https://stackoverflow.com/a/28475234/1719789
if ((viewW / viewH) > (elWidth / elHeight)) {
scale = viewW / elWidth;
} else {
scale = viewH / elHeight;
}
// Center element
const x = ((viewW - ((elWidth) * scale)) / 2);
const y = ((viewH - ((elHeight) * scale)) / 2);
// matrix(scaleX(),skewY(),skewX(),scaleY(),translateX(),translateY())
El.style.transform = 'matrix(' + scale + ', 0, 0, ' + scale + ', ' + x + ', ' + y + ')';
});
And some css:
.Container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#El {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
transform-origin: center center;
transform: translate(-50%, -50%);
transition: transform .3s ease-in-out;
}
The best solution would be getting the current position of the clicked thumbnail (not always centered) and from that scale it to fill the screen. But I'm not really sure where to start to be able to do that.
Is this the effect you are looking for?
CSS:
.Container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
#test {
position: absolute;
top: 50%;
left: 50%;
width: 50%;
transform: translate(-50%, -50%);
transition: all 3.3s ease-in-out;
}
#test.rdy{
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
JS:
// Helper fucntions
const getWindowWidth = () => {
return Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
}
const getWindowHeight = () => {
return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
}
const El = document.getElementById('test');
El.addEventListener('click', () => {
const viewH = getWindowHeight();
const viewW = getWindowWidth();
const elHeight = El.offsetHeight;
const elWidth = El.offsetWidth;
let scale = 1;
if ((viewW / viewH) > (elWidth / elHeight)) {
scale = viewW / elWidth;
} else {
scale = viewH / elHeight;
}
El.style.width = elWidth * scale + 'px';
El.style.height = elHeight * scale + 'px';
//Add .rdy class in case that position needs resetting :)
document.getElementById('test').className += ' rdy';
});
//A hack to make height transition work :)
window.onload = function(){
El.style.height = El.offsetHeight + 'px';
};