Talk about some interesting interactive animations in the Apple marketing page

Talk about some interesting interactive animations in the Apple marketing page

Preface

Two days ago, while browsing Apple’s 16-inch marketing page[1], I found several interesting interactions. I thought that although I am a poor person, knowledge is unlimited, so I studied a wave.

❝ passage about interactivity, so there will be a lot of paper gifmap, we had better be connected to the radio again, I link the sample code on the bottom of the article, there is need for self-created .❞

Two effects

Flip effect

One is the effect of the screen slowly open, the screen in the process opens, "computer graphics" is on the screen stationary until the completion of open or closed when completed let the "Computer Image" With the scroll bar to scroll.

Zoom picture

At the beginning, it is a full-screen picture, and slowly becomes another picture during the scrolling process, and then the picture is gradually reduced with the center of the screen as the reference point. During the zooming process, this picture is fixed on the screen In the center, when zoomed out to a certain value, the picture will scroll with the scroll bar.

Pre-knowledge

Before writing the code, we need to understand a few knowledge points to be used in the next code.

Sticky positioning sticky

It can be simply considered as a mixture of "relative positioning relative" and "fixed positioning fixed" . The element is positioned relative to each other before it crosses the specified range, and then fixed.

stickyElement is fixed relative offset relative to the nearest ancestor element having a scroll box, if not ancestor element can roll, then with respect viewportto calculate the offset element.

one example

The following code, the htmlstructure is as follows:

<body>
  <h1>I am the first demo of sticky</h1>
  <nav>
    <h3>Navigation A</h3>
    <h3>Navigation B</h3>
    <h3>Navigation C</h3>
  </nav>
  <article>
    <p>...</p>
    <p>...</p>
   //...
  </article>
</body>  

The style is as follows:

nav {
  display: table;
  width: 100%;
  position: sticky;
  top: 0;
}

In the code navelements based bodyfor viscous positioned in viewportthe viewport to scroll element topdistance is smaller than 0pxbefore, the relative positioning of elements, that is to say he will scroll with the document. Thereafter, the top element is fixed at a distance 0pxposition.

principle

stickyPrinciple we can look at Zhangxin Xu teacher in-depth understanding of the calculation rule position sticky viscous positioning [2], you can simply look at the teacher explain stickythis figure by the time:

Picture quoted from Zhang Xinxu's in-depth understanding of position sticky

Wherein <nav>is the stickyelement region is a blue box stickyfather element for carrying stickythe element, the red area is <nav>opposite to scroll elements.

  • When the entire blue area is in the red area, the stickyelement has no stickiness effect (Figure 1);
  • When the slide upward slowly, than red blue box rolling elements, the stickyelements will decline in the blue box, to achieve tacky effect (Figure II, III);
  • When the blue box to draw a red box, because stickythe elements in the frame of mind in blue, so there is a direct wave away, no stickiness effect (Figure 3).

In fact, so that we can clearly know why the stickyheight of the element Why not equal to the height of its father, because if they are equal, then tack positioned elements do not have the space to achieve the effect of viscosity, it is equivalent ineffective.

❝ principle above with reference to the Zhangxin Xu teacher in-depth understanding of the calculation rule position sticky viscous positioning [3], the article has to explain the "flow-through cassette" and "sticky bounding rectangle" concept of interpretation, as well as the specific code structure and cssrealization, we can see original. ❞

Common examples

In business, we may encounter such a scenario: that a list, the list of required data according to the time display, and time needs to be fixed at the very top, this time we can use when scrolling stickyto solve this problem:

Specific htmlstructure is as follows:

<body>
  <h1>Time fixed demo</h1>
  <div className={styles.wrapper}>
    <section>
      <h4>May 20</h4>
      <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
      </ul>
    </section>
    <section>
      <h4>May 19</h4>
      <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
      </ul>
    </section>
   //...
</body>

The style is as follows:

body {
  margin: 0px;
  padding: 100px;
  height: 2000px;
}

h4 {
  margin: 2em 0 0;
  background-color: #333;
  color: #fff;
  padding: 10px;
  top: 0;
  z-index: 1;
  position: sticky;
}

As the code, wherein each block is a <section>, and then to <h4>set the stickyposition, so that the above effects can be achieved.

be careful

Of course, the use of stickytime, we need to pay attention to several points:

  • Parent element can not have any overflow:visibleother than the overflowsetting, otherwise no sticky effect. If you set stickyno effect, we can see there is no parent element is set overflow:hidden, remove it.
  • You must specify top, bottom, left, rightone of four values, otherwise it will be in relative positioning.
  • Not be less than the height of the parent element of stickythe element's height (refer to the principle explained above)
  • sticky The element only takes effect within its parent element (refer to the explanation of the principle above)

Another must mention is compatibility, we can use` [4] official website `Can I take a look at stickycompatibility, a red:

In IEcompletely scrap it, if you need to consider the project IE, you will need to fixedbe compatible.

Rolling parallax background-attachment

What is rolling parallax? Take a look at the following example to understand:

" Parallax Scrolling" refers to allowing multiple layers of background to move at different speeds to form a three-dimensional motion effect, bringing a very good visual experience.

The figure above results, we only need one line csscan be achieved, do not need to write complex jscode by setting background-attachment: fixedis complete.

html structure

<body>
  <section className={`${styles.gImg} ${styles.gImg1}`}>IMG1</section>
  <section className={`${styles.gImg} ${styles.gImg2}`}>IMG2</section>
  <section className={`${styles.gImg} ${styles.gImg3}`}>IMG3</section>
</body>

Style code

section {
  height: 100vh;
}

.gImg {
  background-attachment: fixed;
  background-size: cover;
  background-position: center center;
  width: 100%;
}

.gImg1 {
  background-image: url(@/assets/mac1.jpg);
}

.gImg2 {
  background-image: url(@/assets/mac2.jpg);
}

.gImg3 {
  background-image: url(@/assets/mac4.jpg);
}

Parallax scrolling through this csswe can basically achieve the second animation.

❝For the explanation of rolling parallax, you can refer to this article Rolling parallax? CSS is nothing to say [5], it is written in detail. ❞

Canvas Drawing

In fact, second animation we can also use canvaspaint to achieve, we can draw two pictures on a canvas, according to the scroll distance to two pictures show the proportion of the canvas.

It can be canvasprovided drawImageto the drawing method, which provides a variety of ways Canvasto draw an image on.

For example, what we need to achieve is drawn as follows:

In fact, we would need to intercept the first chapter of the upper half of the picture, the next picture in the lower half, and then spliced on ojbkthe look of parameters map:

Here we need to pass in 7 parameters to achieve the effect we need:

ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

The meaning of the specific parameters will not be detailed here. You can refer to the drawImage() MDN document [6].

The idea is roughly to first draw the first picture as a base map, and then we draw the second picture to cover part of the first picture, so that the aforementioned effect can be achieved. Assuming that the original width and height of our picture is 2048*1024, the size of the canvas is 544*341, the offset distance when scrolling is offsetTop, so we can write the following code:

function drawImage() {
  context.drawImage(img1, 0, 0, 2048, 1024, 0, 0, 544, 341);
  context.drawImage(img2, 0, scroll offset distance* 1024/341, 2048, 1024, 0, scroll offset distance, 544, 341);
}

❝I have used it before ctx.drawImage(image, dx, dy, dWidth, dHeight), you can refer to the image preview plug-in written by the author that uses React Hooks to achieve graphite-like image preview [7]. This time 7 parameters are used. You can refer to this article for several ways to draw pictures on canvas[7] 8], very detailed. ❞

transform middle matrix

CSS3 used transformmay transform the elements. Including: displacement, rotation, offset, scaling. transformUse translate/rotate/skew/scalemode control element to transform, it can also be used matrixway to control the conversion element.

for example:

//code one
transform: matrix(1.5, 0, 0, 1.5, 0, 190.5);
//code two
transform: scale(1,5, 1.5) translate(0, 190.5)

The above two lines of code mean the same thing, and we will use this property when we do the second animation.

If you want to understand this property in depth, you can refer to: If you have not studied mathematics in the university, you must understand the matrix in CSS3 transform[9]

Kailu

Initialize the project

If a worker wants to do his job well, he must first sharpen his tools. I used react Hooksto do both animate and use umifast initialization on a project specific initialization steps can refer to the author wrote dva theory to practice - to help you clear away dva knowledge blind spot [10], which details how to use Scaffolding quickly builds a project.

After the construction is completed, the author will put all the examples mentioned before here for everyone to click in and view.

Flip effect

The flip effect is actually very simple, you absolutely can't think of it, how does the Apple marketing page do it?

"It took 120pictures, according to the scroll distance to draw pictures in this scroll position on the display corresponds to" on, you heard it right. I thought it should be before css3controlling the angle of the lid in order to achieve flip effect, I would like, ha ha ha.

Ideas

Then our implementation is very simple, we only need to do the following:

  • First of all, we must define a constant, which specifies the "how much distance to roll" from being covered to fully opened , which we define here as 400px.
  • We need to know when to start flipping or closing the cover. This allows the picture to start animating when it is in the middle of the screen.
//start the scrollTop of the animation
//$('#imgWrapper') is a container for pictures, there are below the html structure
startOpen = $('#imgWrapper').offset().top-(window.innerHeight/2-$('#imgWrapper').height()/2);
  • When flipping or closing the lid, we need to fix the computer in the viewport, wait until it is fully opened or closed, and then let it scroll with the scroll bar. Here we can use it position: sticky.

html structure

<body>
//...
 <div className={styles.stickyContainer}>
   <div className={styles.stickyWrapper}>
     <div id="imgWrapper" className={styles.imgWrapper}>
       <img
          src={require(`@/assets/${asset}.jpg`)}
          alt="Picture 1"
      />
     </div>
   </div>
 </div>
//...
</body>

❝ which introduced dynamic picture we can require(图片路径)be done, such as the above code, we just need to figure out the name of the picture corresponding to the distance required to scroll the display to .❞

Style code

.stickyContainer {
  height: 150vh;
}

.stickyWrapper {
  height: 100vh;
  position: sticky;
  top: 100px;
}

.imgWrapper {
  width: 100vh;
  height: 521px;
  margin: 0 auto;
}

.imgWrapper img {
  width: 100%;
}

Then is calculated to be displayed in the picture at the head of the rolling process is that one we mentioned above: 120pictures, in 400pxthe rolling distance in complete animation.

First of all, after loading is complete, we can get that we can get the scroll value of the starting animation from the top of the document startOpen, so we can get the following code:

useEffect(() => {
 //bind event
  window.addEventListener('scroll', scrollEvent, false);

 //Scroll distance to start animation
 //startOpen
  startOpen = $('#imgWrapper').offset().top-(window.innerHeight/2-$('#imgWrapper').height()/2);

  return ()=>{
    window.removeEventListener('scroll', scrollEvent, false);
  }
}, []);

//scroll event
const scrollEvent = () => {
 //real-time scrollTop
  const scrollTop = $('html').scrollTop();
  let newAsset =''

  if (scrollTop> startOpen && scrollTop <startOpen + 400) {
    let offset = Math.floor((scrollTop-startOpen)/400 * 120);
    
    if (offset <1) {
      offset = 1;
    } else if (offset> 120) {
      offset = 120;
    }

    if (offset <10) {
      newAsset = `large_000${offset}`;
    } else if (offset <100) {
      newAsset = `large_00${offset}`;
    } else {
      newAsset = `large_0${offset}`;
    }
  }

 //boundary value judgment
 //....
  
 //Set image url
  setAsset(newAsset);
};

Preview effect

❝This flip animation is very simple, 120 pictures are replaced, and the corresponding pictures are rendered in real time. In fact, there is no technical content. You can also try other methods to achieve a wave. ❞

Zoom picture

Zoom picture to the screen this animation we can use two ways, one is the "parallax scrolling" to achieve, is a canvasreal-time rendering picture during scrolling.

Before we start, let's take a look at the previous picture without magnification, as follows:

It is composed of two pictures. The picture displayed on the screen has the upper distance between it and the computer case 18px. When enlarged, the upper distance between the picture and the computer case picture should be 18 * 放大比率.

The picture of the computer shell is as follows:

Next, we will begin to introduce two implementation methods.

Canvas achieve

CanvasThe realization of this picture is shown on the screen by the Canvasto draw.

Ideas

In fact, this animation consists of two parts, one is "picture overlay" and the other is "picture reduction" .

  • Picture overlay

Use Canvasto solve Use Canvasrealize we need to use drawImagemethods to draw two pictures on a canvas. You only need to calculate the proportion of the first picture and what proportion of the second picture should be drawn on the canvas at a specific time by the distance of the scroll, and it can be solved. Just need to know when to start the picture overlay.

  • Image zoom out

We used transform: matrixto achieve, which reduce the picture is scaled based on the center point of the screen.

Accordingly, we calculated according to the scroll distance corresponding magnification ratio and translatea value, as shown below, the real-time change transform: matrixthe parameter values on the line.

Here we need to calculate the values ​​of several critical points, such as the maximum/minimum magnification ratio, the maximum/minimum offset value, the point at which the reduction starts, etc.

  • "During the animation, canvaswrap the container should be stickypositioned in the viewport until the end of the animation, canvaswrapped container with a scroll bar will scroll."
Some important values

Here we need to know several values:

  • Defined constant
//The width of the picture displayed by the canvas
const CANVAS_WIDTH = 544;

//The height of the picture displayed by the canvas
const CANVAS_HEIGHT = 341;

//The distance the animation lasts
const ZOOM_SCROLL_RANGE = 400;

//The actual width of the picture displayed by the canvas
const IMG_NATURAL_WIDTH = 2048;

//The actual height of the picture displayed by the canvas
const IMG_NATURAL_HEIGHT = 1024;
  • Enlargement ratio curScale( ), for matrixa scalevalue of

The smallest magnification ratio is 1, which is itself.

The maximum magnification ratio is the screen height divided by the ratio of the screen picture, where I will canvasdraw out of the picture width and height positioning 544 * 341.

const CANVAS_WIDTH = 544;
const CANVAS_HEIGHT = 341;

const scaleRadio = window.innerHeight/CANVAS_HEIGHT;

❝所以放大比率的区间应该是 1 ~ scaleRadio 之间。❞

  • 偏移距离(translate),用于 matrix 的 偏移值

最大的偏移距离,应该是当 curScale 为 1 的时候,包裹元素距离视口顶部的距离,我们的缩放一直都是基于屏幕正中央这个点来进行放大/缩小的,所以可以很简单的得出:

//最大的 translate
let StartScale = 0;
StartScale = window.innerHeight/2 - $('#img-wrapper').height()/2;

最小的偏移距离,应该是在 curScalescaleRadio 时,包裹元素距离视口顶部的距离,这个时候,我们就需要用到之前提到的视屏图片到电脑外壳的 top = 18px 这个值了,因为图片进行了放大,所以最小的偏移距离应该为:

miniTranslate = - 18 * scaleRadio

❝所以偏移距离的区间应该是 miniTranslate ~ StartScale 之间。❞

  • 开始缩放操作的起始点(NewStartScale

其实很简单我们需要在第二章图片完全覆盖掉第一张的图片的时候就进行开始缩放,这个值可以通过 Canvas 包裹元素距离顶部文档的top值」 加上 「一屏的高度」 就能计算出。

let NewStartScale = 0;

NewStartScale = $('#section-sticky-hero').offset().top + window.innerHeight;
Core code

The core code is the calculation when scrolling:

const scrollEvent = () => {
 //current scrollTop
  const scrollTop = $('html').scrollTop();
 //The magnification ratio defaults to the maximum
  let curScale = scaleRadio;
 //The offset distance is the smallest by default
  let translate = -scaleRadio * 18;

 //StartScale: the maximum offset distance
 //NewStartScale: the starting point to start the scaling operation
 //return if not
  if (!NewStartScale || !StartScale) return;

//Calculate the current curScale
 //(scaleRadio-1)/ZOOM_SCROLL_RANGE): how much to zoom in every 1px
 //scrollTop + scaleRadio * 18-NewStartScale: how much is currently scrolled
  curScale = scaleRadio-((scaleRadio-1)/ZOOM_SCROLL_RANGE) * (scrollTop + scaleRadio * 18-NewStartScale);

//boundary value processing
  if (curScale> scaleRadio) {
    curScale = scaleRadio;
  } else if (curScale <1) {
    curScale = 1;
  }

 //Calculate the current translate
 //start from scaleRadio * 18
 //all = scaleRadio * 18 + StartScale
 //Keep adding up during the sliding process
  translate = -scaleRadio * 18 + ((scrollTop + scaleRadio * 18-NewStartScale)/ZOOM_SCROLL_RANGE * (scaleRadio * 18 + StartScale));

//boundary value processing
  if (translate> StartScale) {
    translate = StartScale;
  } else if (translate <-scaleRadio * 18) {
    translate =-scaleRadio * 18;
  }

 //Use canvas to draw pictures
  if (image1 && image2) {
   //In the picture coverage stage
   //curScale is still the largest ratio
    if (curScale === scaleRadio) {
      drawImage({
        img1: image1,
        img2: image2,
        secTop: CANVAS_HEIGHT * (scrollTop + 18 * scaleRadio-NewStartScale)/window.innerHeight,
      });
    } else {
     //If it is not the largest ratio, the picture has been covered
     //Directly display the second chapter
      drawImage({
        img1: image1,
        img2: image2,
        secTop: 0,
      });
    }
  }
 
 //set the style
  $('#img-wrapper').css({
    transform: `matrix(${curScale}, 0, 0, ${curScale}, 0, ${translate})`,
  });
};

html The structure is as follows:

<body>
 //... Other content
  <div id="section-sticky-hero" className={styles.stickyContainer}>
    <div className={styles.componentContainer}>
      <div className={styles.imgWrapper} id="img-wrapper">
        <canvas ref={canvasRef} id="canvas" className={styles.canvas}></canvas>
      </div>
    </div>
  </div>
 //... Other content
</body>

❝ limited space, only the author lists the code and rolling event htmlstructure, other codes, such as drawImagethe method, we are interested, you can refer to the source .❞

Preview effect picture

Rolling parallax implementation

Earlier we also talked about the principles of parallax scrolling, with this background-attachment: fixedattribute, the second half of the movie has essentially been achieved.

Realization ideas

And above canvasdrawing comparison, then, in fact, this step is not the same cover picture, other are basically similar, including the calculation of limit values.

  • Picture overlay

Here we need to set both pictures as background pictures, and at the same time we need to put a computer shell picture on the second picture.

When the first picture fills the screen, give two pictures at the same time plus background-attachment: fixedproperty, not the beginning of time to add this attribute, otherwise it will become the following effects:

  • Image zoom out

Here we do not use transform: matrixto do this zoom, we use background-positionand background-sizeto picture the "reduction/enlargement and offset」 .

  • And others are Canvasvery much the same realization of the principle.
Core code

The scrolling logic code is as follows:

const CANVAS_WIDTH = 544;
const CANVAS_HEIGHT = 341;

const WRAPPER_WIDTH = 694;
const WRAPPER_HEIGHT = 408;

const ZOOM_SCROLL_RANGE = 400;

//scalaRadio
//The maximum magnification of the picture
const scaleRadio = window.innerHeight/CANVAS_HEIGHT;

const scrollEvent = () => {
  const scrollTop = $('html').scrollTop();
  let curScale = scaleRadio;
  let translate = -scaleRadio * 18;

  if (!imgFixFixed || !StartScale) return;

 //The distance between the first picture and the top of the document is imgFixFixed
 //The height of the picture in the first chapter is 100vh, which is the height of one screen
 //So the scrollTop of the picture in Chapter 2 is imgFixFixed + window.innerHeight
  if (scrollTop> imgFixFixed && scrollTop <imgFixFixed + window.innerHeight) {
   //Set the fixed attribute
    setFixImg(true);
  } else {
    setFixImg(false);
  }

 //Suppose the distance we zoomed is 400
 //Then we can calculate the scale of each 1px
 //Then multiply this ratio by the rolling distance
  curScale = scaleRadio-((scaleRadio-1)/ZOOM_SCROLL_RANGE) * (scrollTop-imgFixFixed-window.innerHeight);

 //curScale boundary value processing
 //...

 //start from scaleRadio * 18
 //all = scaleRadio * 18 + StartScale
 //Keep adding up during the sliding process
  translate = -scaleRadio * 18 + ((scrollTop-imgFixFixed-window.innerHeight)/ZOOM_SCROLL_RANGE * (scaleRadio * 18 + StartScale));

 //translate boundary value processing
 //...

 //Set the css style of the picture
 //Scale the image based on the center point
  $('#g-img2').css({
    "width": curScale * CANVAS_WIDTH,
    "height": curScale * CANVAS_HEIGHT,
    "margin-top": `${translate + 18 * curScale}px`,
  });

  $('#img-wrapper').css({
    "width": scaleRadio * WRAPPER_WIDTH,
    "height": scaleRadio * WRAPPER_HEIGHT,
    "background-size": `${curScale * WRAPPER_WIDTH}px ${curScale * WRAPPER_HEIGHT}px`,
    "background-position": `center ${translate}px`,
  });
};

html The structure is as follows:

<body>
 //... Other content
  <section id="g-img" className={`${styles.gImg} ${styles.gImg1} ${fixImg? styles.fixed:''}`}>IMG1</section>

  <div className={styles.stickyContainer}>
    <div className={styles.componentContainer}>
      <div className={styles.imgWrapper} id="img-wrapper">
        <section id="g-img2" className={`${styles.gImg} ${styles.gImg2} ${fixImg? styles.fixed:''}`}>IMG2</section>
      </div>
    </div>
  </div>
 //... Other content
</body>
Preview effect picture

summary

Today I talked about the animations of two Apple marketing pages. The article is not difficult, mainly the application of several basic knowledge points. "Viscous Positioning" , "Scrolling Parallax" , "Canvas Drawing" , "Use of Matrix Properties", etc. I hope it will be helpful to everyone.

Reference: https://cloud.tencent.com/developer/article/1634380 Talk about some interesting interactive animations in the Apple marketing page-Cloud + Community-Tencent Cloud