基于滚动的超酷js图片动画特效

基于滚动的超酷js图片动画特效

这是一款基于滚动的超酷js图片动画特效。该特效在使用鼠标滚动屏幕时,根据屏幕的当前位置,屏幕上的图片做出相应的动画效果,非常炫酷。

使用方法
HTML结构

1
2
3
4
5
6
7
8
9
10
11
12
<div class="container">
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
  <div class="image"></div>
</div>

CSS样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// The container for all images
.container {
  // 2 columns grid
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: 0 10%;
  justify-items: end; // This will align all items (images) to the right
 
  // Fixed positioned, so it won't be affected by default scroll
  // It will be moved using `transform`, to achieve a custom scroll behavior
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
}
// Styles for image elements
// Mainly positioning and background styles
.image {
  position: relative;
  width: 300px;
  height: 100vh;
  background-repeat: no-repeat;
  background-position: center;
 
  // This will align all even images to the left
  // For getting centered positioned images, respect to the viewport
  &:nth-child(2n) {
    justify-self: start;
  }
 
  // Set each `background-image` using a SCSS `for` loop
  @for $i from 1 through 10 {
    &:nth-child(#{$i}) {
     background-image: url('../img/image#{$i}.jpg');
    }
  }
}
// Adjusting layout for small screens
@media screen and (max-width: 760px) {
  .container {
    // 1 column grid
    grid-template-columns: 1fr;
    // Fix image centering
    justify-items: center;
  }
 
  // Fix image centering
  .image:nth-child(2n) {
    justify-self: center;
  }
}

javascript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// Easing function used for `translateX` animation
// From: https://gist.github.com/gre/1650294
function easeOutQuad (t) {
  return t *_ (2 - t)
}
 
// Returns a random number (integer) between __`min`__ and __`max`__
function random (min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min_
}
 
// Returns a random number as well, but it could be negative also
function randomPositiveOrNegative (min, max) {
  return random(min, max) * (Math.random() > 0.5 ? 1 : -1)
}
 
// Set CSS `tranform` property for an element
function setTransform (el, transform) {
  el.style.transform = transform
  el.style.WebkitTransform = transform
}
// Current scroll position
var current = 0
// Target scroll position
var target = 0
// Ease or speed for moving from `current` to `target`
var ease = 0.075
// Utility variables for `requestAnimationFrame`
var rafId = undefined
var rafActive = false
// Container element
var container = document.querySelector('.container')
// Array with `.image` elements
var images = Array.prototype.slice.call(document.querySelectorAll('.image'))
// Variables for storing dimmensions
var windowWidth, containerHeight, imageHeight
 
// Variables for specifying transform parameters (max limits)
var rotateXMaxList = []
var rotateYMaxList = []
var translateXMax = -200
 
// Popullating the `rotateXMaxList` and `rotateYMaxList` with random values
images.forEach(function () {
  rotateXMaxList.push(randomPositiveOrNegative(20, 40))
  rotateYMaxList.push(randomPositiveOrNegative(20, 60))
})
// The `fakeScroll` is an element to make the page scrollable
// Here we are creating it and appending it to the `body`
var fakeScroll = document.createElement('div')
fakeScroll.className = 'fake-scroll'
document.body.appendChild(fakeScroll)
// In the `setupAnimation` function (below) we will set the `height` properly
// The styles for a `div` element (inserted with Javascript)
// Used to make the page scrollable
// Will be setted a proper `height` value using Javascript
.fake-scroll {
  position: absolute;
  top: 0;
  width: 1px;
}
// Geeting dimmensions and setting up all for animation
function setupAnimation () {
  // Updating dimmensions
  windowWidth = window.innerWidth
  containerHeight = container.getBoundingClientRect().height
  imageHeight = containerHeight / (windowWidth > 760 ? images.length / 2 : images.length)
  // Set `height` for the fake scroll element
  fakeScroll.style.height = containerHeight + 'px'
  // Start the animation, if it is not running already
  startAnimation()
}
// Update scroll `target`, and start the animation if it is not running already
function updateScroll () {
  target = window.scrollY || window.pageYOffset
  startAnimation()
}
 
// Listen for `scroll` event to update `target` scroll position
window.addEventListener('scroll', updateScroll)
// Start the animation, if it is not running already
function startAnimation () {
  if (!rafActive) {
    rafActive = true
    rafId = requestAnimationFrame(updateAnimation)
  }
}
// Do calculations and apply CSS `transform`s accordingly
function updateAnimation () {
  // Difference between `target` and `current` scroll position
  var diff = target - current
  // `delta` is the value for adding to the `current` scroll position
  // If `diff < 0.1`, make `delta = 0`, so the animation would not be endless
  var delta = Math.abs(diff) < 0.1 ? 0 : diff * ease
 
  if (delta) { // If `delta !== 0`
    // Update `current` scroll position
    current += delta
    // Round value for better performance
    current = parseFloat(current.toFixed(2))
    // Call `update` again, using `requestAnimationFrame`
    rafId = requestAnimationFrame(updateAnimation)
  } else { // If `delta === 0`
    // Update `current`, and finish the animation loop
    current = target
    rafActive = false
    cancelAnimationFrame(rafId)
  }
 
  // Update images (explained below)
  updateAnimationImages()
 
  // Set the CSS `transform` corresponding to the custom scroll effect
  setTransform(container, 'translateY('+ -current +'px)')
}
// Calculate the CSS `transform` values for each `image`, given the `current` scroll position
function updateAnimationImages () {
  // This value is the `ratio` between `current` scroll position and images `height`
  var ratio = current / imageHeight
  // Some variables for using in the loop
  var intersectionRatioIndex, intersectionRatioValue, intersectionRatio
  var rotateX, rotateXMax, rotateY, rotateYMax, translateX
 
  // For each `image` element, make calculations and set CSS `transform` accordingly
  images.forEach(function (image, index) {
    // Calculating the `intersectionRatio`, similar to the value provided by
    // the IntersectionObserver API
    intersectionRatioIndex = windowWidth > 760 ? parseInt(index / 2) : index
    intersectionRatioValue = ratio - intersectionRatioIndex
    intersectionRatio = Math.max(0, 1 - Math.abs(intersectionRatioValue))
    // Calculate the `rotateX` value for the current `image`
    rotateXMax = rotateXMaxList[index]
    rotateX = rotateXMax - (rotateXMax _ intersectionRatio)
    rotateX = rotateX.toFixed(2)
    // Calculate the _`rotateY`_ value for the current _`image`_
    rotateYMax = rotateYMaxList_[_index]
    rotateY = rotateYMax - (rotateYMax _ intersectionRatio)
    rotateY = rotateY.toFixed(2)
    // Calculate the `translateX` value for the current `image`
    if (windowWidth > 760) {
      translateX = translateXMax - (translateXMax * easeOutQuad(intersectionRatio))
      translateX = translateX.toFixed(2)
    } else {
      translateX = 0
    }
    // Invert `rotateX` and `rotateY` values in case the image is below the center of the viewport
    // Also update `translateX` value, to achieve an alternating effect
    if (intersectionRatioValue < 0) {
      rotateX = -rotateX
      rotateY = -rotateY
      translateX = index % 2 ? -translateX : 0
    } else {
      translateX = index % 2 ? 0 : translateX
    }
    // Set the CSS `transform`, using calculated values
    setTransform(image, 'perspective(500px) translateX('+ translateX +'px) rotateX('+ rotateX +'deg) rotateY('+ rotateY +'deg)')
  })
}
// Listen for `resize` event to recalculate dimmensions
window.addEventListener('resize', setupAnimation)
 
// Initial setup
setupAnimation()
温馨提示:此处内容需要评论本文后才能查看。
4

发表评论