preface

Win10 Metro compared to the previous generation of completely flat style Win8 Metro in animation effect and interactive experience have a relatively large difference, so want to achieve a more realistic Win10 Metro need what animation effect?

Is it really Windows 10 Metro?

Taking a look at this Demo, the seemingly complicated interaction is actually not that difficult to implement. Let’s take a look at the animation effects and implementation principles.

Metro animation effect and implementation

1. 3 d rotation

One notable feature of Win10 Metro is the 3D rotation of the tiles.

The original

implementation

We showed each magnet as a cuboid with a square cross section, and then realized 3D rotation through CSS rotation animation.

html

<div class="scene">
  <div class="box-container">
    <div class="front">
    </div>
    <div class="back">
    </div>
    <div class="top">
    </div>
    <div class="bottom">
    </div>
    <div class="right">
    </div>
    <div class="left">
    </div>
  </div>
</div>
Copy the code

In THE HTML structure, the main function of scene node is to serve as a scene container. With scene, we can make some adjustments to the attributes in the scene. For example, by adjusting perspective, we can enhance or weaken the three-dimensional sense of cuboid when it rotates. Box-container refers to the container node of a cuboid, which contains six surface nodes of a cuboid: front, back, top, bottom, right, and left.

css

.scene {
  position: relative;
  width: 300px;
  height: 150px;
  perspective: 700px;
}
.box-container {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.5 s;
  transform-origin: 50% 50% -75px;
}
.front {
  background-color: rgba,0.5 (3122241);position: absolute;
  width: 300px;
  height: 150px;
}
.back {
  background-color: rgba,3,3,0.5 (241);position: absolute;
  width: 300px;
  height: 150px;
  transform: translateZ(-150px) rotateZ(180deg) rotateY(180deg);
}
.top {
  background-color: rgba,0.5 (3241122);position: absolute;
  width: 300px;
  height: 150px;
  transform: translate3d(0,-75px,-75px) rotateX(90deg);
}
.bottom {
  background-color: rgba,3,0.5 (241241);position: absolute;
  width: 300px;
  height: 150px;
  transform: translate3d(0,75 px, 75 px)rotateX(-90deg);
}
.left {
  background-color: rgba,97,48,0.5 (270);position: absolute;
  width: 150px;
  height: 150px;
  transform: translate3d(-75px,0,-75px) rotateY(-90deg);
}
.right {
  background-color: rgba(30,97,48,0.5);
  position: absolute;
  width: 150px;
  height: 150px;
  transform: translate3d(225px,0,-75px) rotateY(90deg);
}

Copy the code

In CSS, the following points are worth noting

  1. .box-containerThe use of thetransform-style: preserve-3dTo preserve the 3D coordinates of the child nodes in 3D transformation. By using thetransform-origin: 50% 50% -75pxwill.box-containerThe origin of rotation is placed at the center of the cuboid.
  2. In order to make the content on the back of cuboid not reversed after rotation, in.backIn the definition oftransformTo add additionalrotateZ(180deg)Reverse the reverse in advance.
  3. In order to keep each side of the content not becauseThe Z axisWhile zooming in or out, we always keep the current plane facing the observerZ coordinatefor0.

Demo

3D rotation demo

2. Tilt

When the tile is clicked and held, the tile will appear an animation that tilts toward the clicked position.

The original

implementation

If you look closely at the original, you’ll see that the Angle of the face will vary depending on where you press it. When pressing close to the edge of the magnet, the tilt Angle will be larger; When pressed close to the center of the magnet, the tilt Angle will be reduced. By summarizing the above rules, we can draw the following conclusions:

In the center of the magnet as a coordinate system origin (0, 0), when click the position (x, y), on the x axis Angle Ɵ ∝ x | y |, and y axis Angle Ɵ ∝ y | | x.

html

<div class="container">
  <div class="tile">
    <span>Hello World</span>
  </div>
</div>
Copy the code

css

.container {
  width: 200px;
  height: 200px;
  perspective: 700px;
}
.tile {
  background-color: #2d89ef;
  width: 100%;
  height: 100%;
  transition: transform 0.5 s;
  text-align: center;
  color: white;
}
Copy the code

js

const maxTiltAngle = 30; // Set the maximum tilt Angle

const container = document.getElementsByClassName('container') [0];
const tile = document.getElementsByClassName('tile') [0];
const boundingRect = container.getBoundingClientRect();

const tilt = event= > {
  // Calculate the mouse position relative to the container
  const relativeX = event.pageX - (boundingRect.left + window.scrollX);
  const relativeY = event.pageY - (boundingRect.top + window.scrollY);
  // Move the origin from the upper left corner of the container to the center of the container
  const normalizedX = relativeX - boundingRect.width / 2;
  const normalizedY = -(relativeY - boundingRect.height / 2);
  // Calculate the tilt Angle
  const tiltX = normalizedY / (boundingRect.height / 2) * maxTiltAngle;
  const tiltY = normalizedX / (boundingRect.width / 2) * maxTiltAngle;
  / / tilt
  tile.style.transform = `rotateX(${tiltX}deg) rotateY(${tiltY}deg)`;
}

const recover = (a)= > {
  // Restore the tilt
  tile.style.transform = ' ';
}

container.addEventListener('mousedown', tilt);
container.addEventListener('mouseup', recover);
container.addEventListener('mouseleave', recover);

Copy the code

There are several points to note in the implementation of tilting

  1. The center of the magnet with length L and width W is used as the origin of the coordinate system (0, 0). When clicking position (x, y), the formula used in the realization is as follows: the inclination Angle on the X-axis Ɵx = y/(W / 2) * Ɵ Max, and the inclination Angle on the Y-axis Ɵy = x/(L / 2) * Ɵ Max.
  2. It is not enough to restore the tilt only during mouseup events; it also needs to restore the tilt during Mouseleave.

Demo

Tilt the demo

3. Hover halo

When the mouse hovers over the magnet, the magnet has an aperture that moves with the mouse.

The original

implementation

The color of halo gradually fades from the center to the periphery, and the position of halo center will move with the movement of the mouse.

html

<div class="container">
  <div class="hoverLayer">
  </div>
  <div class="hoverGlare">
  </div>
</div>
Copy the code

css

.container {
  position: relative;
  background-color: # 000;
  width: 200px;
  height: 200px;
  overflow: hidden;
}

.hoverLayer {
  position: absolute;
  z-index: 1;
  width: 100%;
  height: 100%;
}

.hoverGlare {
  position: absolute;
  background-image: radial-gradient(circle at center, rgba(255,255,255, 0.7) 0%.rgba,0.1 (255255255)100%);
  transform: translate(-100px, -100px);
  width: 400px;
  height: 400px;
  opacity: 0.4;
}
Copy the code

js

const boundingRect = document.getElementsByClassName('container') [0].getBoundingClientRect();

const hoverGlare = document.getElementsByClassName('hoverGlare') [0];

const glare = event= > {
  // Calculate the mouse position relative to the container
  const relativeX = event.pageX - (boundingRect.left + window.scrollX);
  const relativeY = event.pageY - (boundingRect.top + window.scrollY);
  // Move the origin from the upper left corner of the container to the center of the container
  const normalizedX = relativeX - boundingRect.width / 2;
  const normalizedY = relativeY - boundingRect.height / 2;
  // Adjust the halo transparency and position
  hoverGlare.style.opacity = 0.4;
  hoverGlare.style.transform = `translate(${normalizedX}px, ${normalizedY}px) translate(-${boundingRect.width / 2}px, -${boundingRect.height / 2}px)`;
}

const resetGlare = (a)= > {
  // Hide the halo
  hoverGlare.style.opacity = 0;
}

const hoverLayer = document.getElementsByClassName('hoverLayer') [0];

hoverLayer.addEventListener('mousemove', glare);
hoverLayer.addEventListener('mouseleave', resetGlare);
Copy the code

In the implementation of halo, the following points need to be noted

  1. We usez-indexfor1the.hoverLayerAs the mouse event node, to avoid the problem of inaccurate mouse positioning because the child node overwrites the parent node.
  2. We created a halo suspension twice the width and height of the container and passed throughtranslateMove the suspension layer to achieve efficient halo position shifting.

Demo

Hover halo demo

4. Click the ripple

When the mouse clicks on the magnet, a ripple animation will be formed in the clicking position and spread outwards

The original

implementation

We can see that the circular ripple starts from the click and spreads outward until it dissipates.

html

<div class="tile">
  <div class="clickGlare">
  </div>
</div>
Copy the code

css

.tile {
  position: relative;
  width: 200px;
  height: 200px;
  background-color: # 000;
  overflow: hidden;
}

.clickGlare {
  position: absolute;
  width: 90px;
  height: 90px;
  border-radius: 50%;
  opacity: 0;
  filter: blur(5px);
  background-image: radial-gradient(rgba (255, 255, 255, 0.7)0%.rgba(255, 255, 255, 0) 100%);
}

.ripple {
  animation-name: ripple;
  animation-duration: 1.3 s;
  animation-timing-function: ease-in;
}

@keyframes ripple {
  0% {
    opacity: 0.5; 100%} {transform: scale(5);
    opacity: 0; }}Copy the code

js

const tile = document.getElementsByClassName('tile') [0];
const boundingRect = tile.getBoundingClientRect();
const clickGlare = document.getElementsByClassName('clickGlare') [0];

const ripple = event= > {
  // This parameter is executed only when the class of the node does not contain ripple
  if (clickGlare.classList.contains('ripple')) return;
  // Calculate the mouse position relative to the container
  const relativeX = event.pageX - (boundingRect.left + window.scrollX);
  const relativeY = event.pageY - (boundingRect.top + window.scrollY);
  // Adjust the center position of the ripple according to the mouse position
  clickGlare.style.top = `${relativeY - 45}px`;
  clickGlare.style.left =  `${relativeX - 45}px`;
  // Add a ripple animation
  clickGlare.classList.add('ripple');
}

const resetRipple = (a)= > {
  // Remove the ripple animation
  clickGlare.classList.remove('ripple');
}

tile.addEventListener('mousedown', ripple);
clickGlare.addEventListener('animationend', resetRipple);
Copy the code

There are several points to note in the implementation of the click ripple

  1. To make the ripple animation more similar to the original, we usedfilter: blurThis CSS, this CSS may cause compatibility issues in older browsers or IE/Edge.

Demo

Click on the demo of the ripple

summary

If we put the above animations together, we can achieve a more realistic Windows 10 Metro layout. Isn’t Windows 10 Metro as simple as it looks?

The animations included in Windows 10 tiles are broken down to some of the more common animations that enhance the user experience. You can also try to imitate these simple animations in your daily projects or work to make interaction and design more friendly.

Finally, I also made a widget using Vue, which is convenient for you to implement Win 10 Metro layout in Vue. Welcome to exchange and discuss