preface
- The case code in this article is not original.
- This article focuses on how parallax effects are implemented (the original three-piece set) and does not cover performance optimization (call me a bully).
- This paper will start from the principle, and then combine multiple cases to achieve the final effect.
- Give me a thumbs up after I quit school
Don’t miss the “recommendations” section of this article too, because everyday developers rarely use the native way to do parallax effects.
🐱💻 online demo
🐱👓 This article code repository
This example is shown in the figure below
The principle of
-
PC side parallax effect is usually based on the mouse, keyboard, scrollbar changes and operation of visual differentiation control.
-
There may also be gravitational gyroscopic interactions on the mobile end, which is not covered in this article.
Here are some examples:
- Hover over the top left of the screen: an element flies to the bottom right of the screen (opposite to the mouse).
- Slide down the page: the background image does not move and other elements, such as text elements, move up.
- …
Let me show you a couple of examples to give you an intuition:
Click to jump online preview: ⭐⭐ Parallax effect of mouse movement ⭐⭐
[Click to jump online preview: Using GSAP’s Parallax layer effect effects (scroll with mouse wheel)]
[Click jump online preview: Tilt effect (mouse movement)]
[Click to jump online preview: Creative advertising parallax effect (mouse movement)]
implementation
Understand the implementation of the principle, the implementation of the key is the event listener addEventListener.
A simple example
Let’s play with a simple example
This example works like this: when the mouse moves to the left, the element moves to the right. When the mouse moves up, the element moves down.
<style>
.box {
width: 200px;
height: 300px;
background: lightblue;
position: absolute; /* Absolute position */
}
</style>
<div class="box"></div>
<script>
// Get the.box element
const box = document.querySelector('.box')
// Event listener for the entire document (mouse over mousemove)
document.addEventListener('mousemove'.e= > {
// Get the x and y coordinates of the current mouse position, set right and bottom of the. Box respectively
box.style.right = e.clientX + 'px'
box.style.bottom = e.clientY + 'px'
})
</script>
Copy the code
When the mouse is in the top left of the page (add x and Y coordinates 10 and 20, respectively), place the element in the bottom right of the page (right: 10, down: 20).
That’s what the Principia tells us to do.
If you don’t understand clientX and clientY, check out the following article:
JS event objectclientX
, clientY
, screenX
, screenY
, offsetX
, offsetY
The difference between”
Note:
This example uses right and left to move elements. The reason for doing this is to explain and implement it in the simplest way possible.
In practice, this can lead to layout issues and performance issues (resulting in layout changes or redrawing, and unstable animations). It is recommended to use transforms for operations such as moves on elements.
premium
In addition to moving elements, you can also move background positions, rotate elements, and so on.
Also consider the animation magnitude of the element. Examples like the one above have no control over how much the element moves, so when the mouse moves to the far right or bottom of the screen, the element goes off the screen. This may not be a good operating experience.
When it comes to animation amplitude, consider the matter of reference. Common references are browser width, container width, container location, and so on.
Take this example:
This example looks like a lot of elements to manipulate, but it’s actually quite simple to break them down layer by layer.
Factors to consider include:
- Container rotation
- The background image moves slightly
- The character moves with the mouse
The reference point is the ratio of the mouse position to the width and height of the document, and the formulas you set limit the range of elements that can be moved or rotated.
1. Container rotation
To create adiv
Container, set the shadow.
<div class="card"></div>
<style>
html.body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
/ * * / container
.card {
width: 175px;
height: 250px;
border-radius: 8px;
box-shadow: 0px 10px 20px 20px rgba(0.0.0.0.17);
}
</style>
Copy the code
Container rotation is controlled by JS
// Get the container element
const card = document.querySelector('.card')
// Compute the function
function computedTransform(num, doc) {
return (num / doc * 40 - 20).toFixed(1)}// Add a mouse movement event listener to the document
document.addEventListener('mousemove'.e= > {
// Rotate the container
card.style.transform = `
rotateX(${computedTransform(e.clientX, window.innerWidth)}deg)
rotateY(${computedTransform(e.clientY, window.innerHeight)}deg)
`
})
Copy the code
2. Move the background
Add background image
<! -- omit some duplicate code -->
<style>
.card {
width: 175px;
height: 250px;
border-radius: 8px;
box-shadow: 0px 10px 20px 20px rgba(0.0.0.0.17);
background-image: url(./img/3dr_spirited.jpg);
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 110% 110%;
}
</style>
Copy the code
This section of CSS focuses on the last 4 lines added (background related).
Add a background image directly to the CSS using background-image. The background image does not repeat itself, and starts in the center. The background image is slightly larger than the container, but does not exceed the container.
JS control: The background image also moves with the mouse
Recorded GIF a little bit of a problem, the last mouse residual shadow, just look at the effect.
/* omit some duplicate code */
const card = document.querySelector('.card')
/ / calculate
function computedBGPosition(num, doc) {
return (60 - Number((num / doc * 20).toFixed(1)) + The '%')}// Add a mouse movement event listener to the document
document.addEventListener('mousemove'.e= > {
// Move the background
card.style.backgroundPosition = `
${computedBGPosition(e.clientX, window.innerWidth)}
${computedBGPosition(e.clientY, window.innerHeight)}
`
})
Copy the code
I controlled the movement range of this part within a relatively small range and used background-position to control the starting position of the background image.
Combine this with the code for “1, container rotation”, and you get something like this:
3. Moving pictures (people)
The character moves with the mouse
The complete code
<style>
html.body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
/ * * / container
.card {
width: 175px;
height: 250px;
overflow: hidden;
background-image: url(./img/3dr_spirited.jpg);
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 110% 110%;
transform-origin: 50% 50%;
perspective: 1800px;
transform-style: preserve-3d;
border-radius: 8px;
box-shadow: 0px 10px 20px 20px rgba(0.0.0.0.17);
}
/* Image style (1000) */
.card img {
height: 100%;
position: relative;
top: 25px;
left: 25px;
}
</style>
<div class="card">
<img src="./img/3dr_chihiro.png" alt="">
</div>
<script>
/ / container
const card = document.querySelector('.card')
// Character (Xiao Qian)
const img = card.querySelector('img')
// Move the background image calculation method
function computedBGPosition(num, doc) {
return (60 - Number((num / doc * 20).toFixed(1)) + The '%')}/ / calculate the translate
function computedTransform(num, doc) {
return (num / doc * 40 - 20).toFixed(1)}// Add a mouse movement event listener to the document
document.addEventListener('mousemove'.e= > {
// Rotate the container
card.style.transform = `
rotateX(${computedTransform(e.clientX, window.innerWidth)}deg)
rotateY(${computedTransform(e.clientY, window.innerHeight)}deg)
`
// Move the background
card.style.backgroundPosition = `
${computedBGPosition(e.clientX, window.innerWidth)}
${computedBGPosition(e.clientY, window.innerHeight)}
`
// Move the image
img.style.transform = `
translateX(${computedTransform(e.clientX, window.innerWidth)}px)
translateY(${computedTransform(e.clientY, window.innerHeight)}px)
`
})
</script>
Copy the code
Style section:
- Container: Needs to be set
overflow: hidden;
The picture is not displayed in excess of the moving part - Character picture: Character needs to be set
position: relative;
“And move it down a bit to hide the lower body.
JS:
const img = card.querySelector('img')
/ / calculate the translate
function computedTransform(num, doc) {
return (num / doc * 40 - 20).toFixed(1)
}
img.style.transform = `
translateX(${computedTransform(e.clientX, window.innerWidth)}px)
translateY(${computedTransform(e.clientY, window.innerHeight)}px)
`
Copy the code
This part is mainly added to calculate the distance of picture movement by the current position of mouse and the width and height of the screen.
ultimate
The “advanced version” above explains the secrets of implementing parallax effects.
Usually see more complex effects, in fact, you can split elements one by one, one by one control.
For example, the final version of this article:
This part of the explanation is in the code comments, it is recommended to build a project to run.
If you don’t know anything, you can discuss it in the comments section.
The complete code is shown below:
<style>
.page__x {
width: 1000px;
height: 700px;
/* Center layout */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
/* Sets the view where the element is viewed */
perspective: 1800px;
/* Background color */
background: #642b73;
background: linear-gradient(to bottom, #c6426e.#642b73);
}
/* Popular */
h1 {
/* Bottom margin */
margin-bottom: 30px;
/* Z offset */
transform: translateZ(35px);
/* Letter spacing */
letter-spacing: -1px;
/ * name * /
font-size: 32px;
/* Font size */
font-weight: 800;
/* Font color */
color: #3e3e42;
}
/* Movies */
h3 {
/* Bottom margin */
margin-bottom: 6px;
/* Z offset */
transform: translateZ(25px);
/ * name * /
font-size: 16px;
/* Font color */
color: #eb285d;
}
/* Card main container */
.cards {
/* Inline block element */
display: inline-block;
/* Minimum width */
min-width: 595px;
/* Inner margin */
padding: 30px 35px;
/* Sets the view where the element is viewed */
perspective: 1800px;
/* Rotate base point */
transform-origin: 50% 50%;
/* Makes the converted child retain its 3D transformation */
transform-style: preserve-3d;
/ * rounded corners * /
border-radius: 15px;
/* Left-align text */
text-align: left;
/* Background color */
background: #fff;
* / / * projection
box-shadow: 0px 10px 20px 20px rgba(0.0.0.0.17);
}
/ * * / CARDS
.card {
/* Inline block element */
display: inline-block;
/ * * / wide
width: 175px;
/* 高 */
height: 250px;
/* Relative positioning */
position: relative;
/* Hide the overflow */
overflow: hidden;
/* Sets the view where the element is viewed */
perspective: 1200px;
/* Makes the converted child retain its 3D transformation */
transform-style: preserve-3d;
/* Z offset */
transform: translatez(35px);
/ * * /
transition: transform 200ms ease-out;
/* Text is centered */
text-align: center;
/ * rounded corners * /
border-radius: 15px;
* / / * projection
box-shadow: 5px 5px 20px -5px rgba(0.0.0.0.6);
}
/* All cards except the last one */
.card:not(:last-child) {
/* Right margin */
margin-right: 30px;
}
/* Card image */
.card__img {
/* Relative positioning */
position: relative;
/ * * /
height: 100%;
}
/* Card background */
.card__bg {
bottom: -50px;
left: -50px;
position: absolute;
right: -50px;
top: -50px;
/* Rotate base point */
transform-origin: 50% 50%;
transform: translateZ(-50px);
z-index: 0;
}
/* Princess Mononoke */
.princess-mononoke .card__img {
top: 14px;
right: -10px;
height: 110%;
}
/* Princess Mononoke background */
.princess-mononoke .card__bg {
background: url("img/3dr_monobg.jpg") center/cover no-repeat;
}
/* Spirited Away */
.spirited-away .card__img {
top: 25px;
}
/* Spirited Away background */
.spirited-away .card__bg {
background: url("img/3dr_spirited.jpg") center/cover no-repeat;
}
/* Hal's moving castle */
.howl-s-moving-castle .card__img {
top: 5px;
left: -4px;
height: 110%;
}
/* Howl's moving castle background */
.howl-s-moving-castle .card__bg {
background: url("img/3dr_howlbg.jpg") center/cover no-repeat;
}
/* The text of the card */
.card__text {
/* Elastic layout */
display: flex;
/* The main axis is vertical */
flex-direction: column;
/* Spindle center aligned */
justify-content: center;
/* Align the midpoint of the cross axis */
align-items: center;
/ * * / wide
width: 100%;
/* 高 */
height: 70px;
/* Absolute position */
position: absolute;
/* Stack order */
z-index: 2;
/* Distance from the bottom */
bottom: 0;
/* Background color: gradient */
background: linear-gradient(to bottom,
rgba(0.0.0.0) 0%.rgba(0.0.0.0.55) 100%);
}
/* Card title */
.card__title {
/* Bottom margin */
margin-bottom: 3px;
/* Set the left and right margins to 10px */
padding: 0 10px;
/ * name * /
font-size: 18px;
/* Font size */
font-weight: 700;
/* Font color */
color: #fff;
}
</style>
<div class="page__x" id="pageX">
<div class="cards">
<h3>Movies</h3>
<h1>Popular</h1>
<! -- Princess Mononoke
<div class="card princess-mononoke">
<div class="card__bg"></div>
<img class="card__img" src="./img/3dr_mono.png" />
<div class="card__text">
<p class="card__title">Princess Mononoke</p>
</div>
</div>
<! -- Spirited Away
<div class="card spirited-away">
<div class="card__bg"></div>
<img class="card__img" src="./img/3dr_chihiro.png" />
<div class="card__text">
<p class="card__title">Spirited Away</p>
</div>
</div>
<! -- Hal's moving castle
<div class="card howl-s-moving-castle">
<div class="card__bg"></div>
<img class="card__img" src="./img/3dr_howlcastle.png" />
<div class="card__text">
<p class="card__title">Howl's Moving Castle</p>
</div>
</div>
</div>
</div>
<script>
// Page container
const pageX = document.querySelector('#pageX')
// Card container
const cards = document.querySelector('.cards')
// All images
const images = document.querySelectorAll('.card__img')
// All backgrounds
const backgrounds = document.querySelectorAll('.card__bg')
// Rotate Angle coefficient
let range = 40
// Rotation formula (return -20 ~ 20, keep 1 as decimal)
let calcValue = (a, b) = > (a / b * range - range / 2).toFixed(1)
// Undefined is returned by default
let timeout = void 0
// Parallax animation function
// e: parameter of mouse movement event
function parallax(e) {
let x = e.x // indicates the X-axis position
let y = e.y // indicates the y position
/ / if the timeout already exists, will cancel a previously by calling the window. The requestAnimationFrame () method is added to the request of animation frames of the plan.
if (timeout) {
// This is an experimental feature that is still under development in some browsers
window.cancelAnimationFrame(timeout);
}
// Call the specified callback to update the animation before the next redraw
timeout = window.requestAnimationFrame(function () {
// Use calcValue to calculate the value based on the current mouse position and the container aspect ratio
let xValue = calcValue(x, pageX.offsetWidth)
let yValue = calcValue(y, pageX.offsetHeight)
// Set the rotation Angle of the card container
cards.style.transform = "rotateX(" + yValue + "deg) rotateY(" + xValue + "deg)";
// Set the displacement of all images
images.forEach(item= > {
item.style.transform = "translateX(" + -xValue + "px) translateY(" + yValue + "px)"
})
// Set the position of all background images
backgrounds.forEach(item= > {
item.style.backgroundPosition = xValue * 45. + "px " + -yValue * 45. + "px"})})}window.onload = () = > {
// Listen for mouse movement in the pageX container
pageX.addEventListener('mousemove', parallax, false)}// Remove the listener away from the page
window.onbeforeunload = () = > {
pageX.removeEventListener('mousemove', parallax)
}
</script>
Copy the code
Supplementary notes on Knowledge points
JS event objectclientX
, clientY
, screenX
, screenY
, offsetX
, offsetY
The difference between”
JS event listeneraddEventListener()
“
JS removes event listenersremoveEventListener()
“
recommended
Parallax effects are rarely implemented in a native way in daily development.
A lightweight JS animation library is recommended: “animos.js”
The usage of this library is too simple, directly read the “official documentation” to know how to use, this article will not explain.