Zoom with slider in a background-image - javascript

I want to create a vertical zoom slide on a background-image, which is being used behind a .png image. And I have no idea how to solve the problem.
Solve the problem using jsx, css in the file, knowing that there is already another function in the React app.
import React from "react";
import Vestido from '../img/vestido.png';
//import Estampa from '../img/estampaTest.jpg';
import { Img, DivGeral } from "../style/styled";
export default function Foto() {
document.addEventListener('dragover', (event) =>{
const objeto = document.getElementById('module');
// Grab the mouse's X-position.
const mousex = event.offsetX;
// Grab the mouse's Y-position.
const mousey = event.offsetY;
objeto.style.backgroundPosition = mousex + 'px ' + mousey + 'px';
});
return (
<DivGeral>
<div>
<Img src={Vestido} alt='camiseta' class='module' id='module'/>
</div>
</DivGeral>
)}
import styled from 'styled-components'
import Estampa from '../img/estampaTest2.jpg'
import Vestido from '../img/vestido.png'
export const Img = styled.img`
--size : 46%;
width: 50%;
height: 50%;
background-image: url(${Estampa});
background-size: var(--size);
background-repeat: repeat;
-webkit-mask-image: url(${Vestido}) center/cover ;
mask-image: url(${Vestido}) center/cover;
`

Related

React sticky canvas trying to hide it

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;

How to implement open curtain animation

I am trying to copy an open curtain animation like in the following where the side divs expand horizontally on the background image on scroll.
I have a working example with the following code
import { useState, useEffect, useRef, useCallback } from "react";
import "./styles.css";
export default function App() {
// check window scroll direction
const [y, setY] = useState(null);
const [scrollDirection, setScrollDirection] = useState("");
const boxTwo = useRef(null);
const boxTwoLeft = useRef(null);
const boxTwoRight = useRef(null);
const countRefTranslateX = useRef(0);
// check window scroll direction https://stackoverflow.com/questions/62497110/detect-scroll-direction-in-react-js
const handleScrollDirection = useCallback(
(e) => {
const window = e.currentTarget;
if (y > window.scrollY) {
setScrollDirection("up");
} else if (y < window.scrollY) {
setScrollDirection("down");
}
setY(window.scrollY);
},
[y]
);
const handleScroll = useCallback(() => {
if (boxTwo.current) {
let position = boxTwo.current.getBoundingClientRect();
// checking for partial visibility and if 50 pixels of the element is visible in viewport
if (
position.top + 50 < window.innerHeight &&
position.bottom >= 0 &&
scrollDirection === "down"
) {
countRefTranslateX.current = countRefTranslateX.current + 3;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
} else if (
position.top + 50 < window.innerHeight &&
position.bottom >= 0 &&
scrollDirection === "up"
) {
countRefTranslateX.current = countRefTranslateX.current - 3;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
} else {
countRefTranslateX.current = 0;
boxTwoLeft.current.style.transform = `translateX(-${countRefTranslateX.current}px)`;
boxTwoRight.current.style.transform = `translateX(${countRefTranslateX.current}px)`;
}
}
}, [scrollDirection]);
useEffect(() => {
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [handleScroll]);
useEffect(() => {
setY(window.scrollY);
window.addEventListener("scroll", handleScrollDirection);
return () => {
window.removeEventListener("scroll", handleScrollDirection);
};
}, [handleScrollDirection]);
return (
<div className="App">
<div className="boxOne"></div>
<div ref={boxTwo} className="boxTwo">
<div ref={boxTwoLeft} className="boxTwoLeft"></div>
<div ref={boxTwoRight} className="boxTwoRight"></div>
</div>
<div className="boxThree"></div>
</div>
);
}
I have two issues.
My right white div keeps going to the left while I scroll up
I cannot get a fixed vertical window breakpoint. My animation continues after the window has scrolled after the point I want to start/stop moving the divs
How can I resolve these issues?
My codesandbox
I've looked over what you've done but it's way too complicated.
All you want is to place two curtains (panels) on top of your content.
The container should have position:relative and the curtains should have position: absolute.
You then declare the scrollLimits in between which they should move. In the example below, 0 is the starting point and window.innerHeight the end point. Replace those values with whatever makes sense for your section, considering its vertical position in the page. You could use the section's current offsetTop and clientHeight to set the limits dynamically, based on current window size.
You then get the current scroll position and calculate the scroll percentage relative to the limits.
You then apply the percentage/2 + 50% to each curtain's transform: translateX() the left one with negative value, the right one with positive value.
Done.
document.addEventListener('scroll', revealSection);
/* function revealSection() {
const scrollLimits = [0, window.innerHeight];
const currentScroll = window.scrollY;
const percent = Math.min(currentScroll * 100 / (scrollLimits[1] - scrollLimits[0]), 100);
document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
} */
function revealSection() {
const top = document.querySelector('section').getBoundingClientRect().top;
const percent = Math.min(Math.max(0, (window.innerHeight - top) / window.innerHeight * 100), 100);
document.querySelector('.l-panel').style.transform = `translateX(-${percent/2 + 50}%)`;
document.querySelector('.r-panel').style.transform = `translateX(${percent/2 + 50}%)`;
}
body {
margin: 0;
}
section {
background-image: url('https://static.toss.im/assets/homepage/new tossim/section2_4_big.jpg');
background-repeat: no-repeat;
background-position: center;
background-size: cover;
width: 100%;
height: 100vh;
margin: 100vh 0;
position: relative;
overflow: hidden;
}
.l-panel, .r-panel{
position: absolute;
height: 100%;
width: 100%;
content: '';
top: 0;
background-color: white;
left: 0;
}
<section>
<div class="l-panel"></div>
<div class="r-panel"></div>
</section>
Note: change the CSS selectors so the styles apply to your section only.
Edit: I've come up with a more generic way to set the scroll interval, using the section's getBoundingClientRect().top and window.innerHeight. It's probably more useful, as you no longer have to worry about the section's position in page.
I've left the previous version in, for reference and/or for anyone who prefers it.

How to make a zoom effect on part of an image?

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>

Convert React-Spring Example to Styled-Components

Tried copying this example, but I'm using gatsby & styled components; So I'm trying to convert the plain css file to styled-components. However, the cards aren't moving or reacting at all when I click or drag, did I do something horribly wrong?
This file is pretty much just copy/paste:
import React, { useState } from "react";
import { useSprings, animated, interpolate } from "react-spring";
import { useGesture } from "react-use-gesture";
const cards = [
"https://upload.wikimedia.org/wikipedia/en/f/f5/RWS_Tarot_08_Strength.jpg",
"https://upload.wikimedia.org/wikipedia/en/5/53/RWS_Tarot_16_Tower.jpg",
"https://upload.wikimedia.org/wikipedia/en/9/9b/RWS_Tarot_07_Chariot.jpg",
"https://upload.wikimedia.org/wikipedia/en/d/db/RWS_Tarot_06_Lovers.jpg",
"https://upload.wikimedia.org/wikipedia/en/thumb/8/88/RWS_Tarot_02_High_Priestess.jpg/690px-RWS_Tarot_02_High_Priestess.jpg",
"https://upload.wikimedia.org/wikipedia/en/d/de/RWS_Tarot_01_Magician.jpg"
];
// These two are just helpers, they curate spring data, values that are later being interpolated into css
const to = i => ({
x: 0,
y: i * -4,
scale: 1,
rot: -10 + Math.random() * 20,
delay: i * 100
});
const from = i => ({ x: 0, rot: 0, scale: 1.5, y: -1000 });
// This is being used down there in the view, it interpolates rotation and scale into a css transform
const trans = (r, s) =>
`perspective(1500px) rotateX(30deg) rotateY(${r /
10}deg) rotateZ(${r}deg) scale(${s})`;
function Deck() {
const [gone] = useState(() => new Set()); // The set flags all the cards that are flicked out
const [props, set] = useSprings(cards.length, i => ({
...to(i),
from: from(i)
})); // Create a bunch of springs using the helpers above
// Create a gesture, we're interested in down-state, delta (current-pos - click-pos), direction and velocity
const bind = useGesture(
({
args: [index],
down,
delta: [xDelta],
distance,
direction: [xDir],
velocity
}) => {
const trigger = velocity > 0.2; // If you flick hard enough it should trigger the card to fly out
const dir = xDir < 0 ? -1 : 1; // Direction should either point left or right
if (!down && trigger) gone.add(index); // If button/finger's up and trigger velocity is reached, we flag the card ready to fly out
set(i => {
if (index !== i) return; // We're only interested in changing spring-data for the current spring
const isGone = gone.has(index);
const x = isGone ? (200 + window.innerWidth) * dir : down ? xDelta : 0; // When a card is gone it flys out left or right, otherwise goes back to zero
const rot = xDelta / 100 + (isGone ? dir * 10 * velocity : 0); // How much the card tilts, flicking it harder makes it rotate faster
const scale = down ? 1.1 : 1; // Active cards lift up a bit
return {
x,
rot,
scale,
delay: undefined,
config: { friction: 50, tension: down ? 800 : isGone ? 200 : 500 }
};
});
if (!down && gone.size === cards.length)
setTimeout(() => gone.clear() || set(i => to(i)), 600);
}
);
// Now we're just mapping the animated values to our view, that's it. Btw, this component only renders once. :-)
return props.map(({ x, y, rot, scale }, i) => (
<animated.div
key={i}
style={{
transform: interpolate([x, y], (x, y) => `translate3d(${x}px,${y}px,0)`)
}}
>
{/* This is the card itself, we're binding our gesture to it (and inject its index so we know which is which) */}
<animated.div
{...bind(i)}
style={{
transform: interpolate([rot, scale], trans),
backgroundImage: `url(${cards[i]})`
}}
/>
</animated.div>
));
}
export default Deck;
And here, I am trying to split the CSS file into (two) styled components:
So it's basically more like a wrapper:
import React from "react";
import Tarot from "#components/SpringTarot";
import styled from "styled-components";
const Outer = styled.div`
* {
box-sizing: border-box;
}
overscroll-behavior-y: contain;
margin: 0;
padding: 0;
height: 100%;
width: 100%;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir,
helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif;
position: fixed;
overflow: hidden;
`;
const Wrapper = styled.div`
background: lightblue;
position: fixed;
overflow: hidden;
width: 100%;
height: 100%;
cursor: url("https://uploads.codesandbox.io/uploads/user/b3e56831-8b98-4fee-b941-0e27f39883ab/Ad1_-cursor.png")
16 16,
auto;
& > div {
position: absolute;
width: 100vw;
height: 100vh;
will-change: transform;
display: flex;
align-items: center;
justify-content: center;
}
& > div > div {
background-color: white;
background-size: auto 85%;
background-repeat: no-repeat;
background-position: center center;
width: 45vh;
max-width: 300px;
height: 85vh;
max-height: 570px;
will-change: transform;
border-radius: 10px;
box-shadow: 0 12.5px 100px -10px rgba(50, 50, 73, 0.4),
0 10px 10px -10px rgba(50, 50, 73, 0.3);
}
`;
const SpringTarot = () => (
<Outer>
<Wrapper>
<Tarot />
</Wrapper>
</Outer>
);
export default SpringTarot;
I have tried putting what's right now in the outer wrapper into the styled-component global styles, but it makes no difference.
Make sure to extend the native element you want to animate using animated. If you want to animate React components, styled-components, or elements on other platforms, then do this:
const AnimatedHeader = styled(animated.h1)`
...;
`
This is from the official Spring docs.

How to change the position of the element dynamically in ReactJs?

I am using ReactJs. I want to change a position of the image dynamically.
I am trying to set up style like described here:
My sprite.js:
'use strict';
import '../res/my_css.css'
const React = require('react');
const ReactDOM = require('react-dom');
class Sprite extends React.Component {
render() {
var left = 5000 + 'px';
var top = 5000 + 'px';
var padding = 5000 + 'px';
return (
<div id="bird" style={{padding:padding, left: left, top:top}}/>
)
}
}
export default Sprite;
ReactDOM.render(
<Sprite />,
document.getElementById('sprite')
)
My css contains:
#bird {
background:url('../res/bird.png');
background-repeat: no-repeat;
}
I see the image, but style={{padding:padding, left: left, top:top}} does not applied.
If I look to the source code in Chrome, I see the style padding:5000px, left:5000px, top:5000px. But my image is in the left top corner.
How can I dinamically change the position of the div element (image in my case) in ReactJs?
Try like this.You have not included position:absolute
class Sprite extends React.Component {
render() {
var left = 5000 + 'px';
var top = 5000 + 'px';
var padding = 5000 + 'px';
return (
<div id="bird" style={{padding, left, top,position:'absolute'}}/>
)
}
}
To play with left and top add position like this
<div id="bird" style={{position: 'relative', padding, left, top}}/>

Categories

Resources