CSS3 3D foundation

Coordinate system

For 3D transformation, the most basic must be the coordinate system. There are two completely different coordinate systems in 3D: left-handed and right-handed.

  • The right hand coordinate system is X to the right, Y to the up, and Z to self.
Stretch out your right hand so that your thumb and forefinger form an "L", thumb to the right and forefinger up. The rest of your fingers point towards yourself, thus establishing a right-handed coordinate system. The thumb, index finger and other fingers represent the positive direction of the X, Y and Z axes respectivelyCopy the code
  • The left-hand Z axis, on the other hand, is facing away from itself. In computers, the left-hand axis is usually used, while in mathematics, the right-handed axis is usually used.
Stretch out your left hand so that your thumb and forefinger form an "L", thumb to the right and forefinger up. The remaining fingers point forward, thus establishing a left - handed coordinate system. The thumb, index finger and other fingers represent the positive direction of the X, Y and Z axes respectivelyCopy the code

The 3D coordinate system in CSS3 is slightly different from the left hand coordinate system above. It is equivalent to being rotated 180 degrees around the X axis. Try to rotate the left hand around the thumb direction until the index finger points directly below, at which point the thumb, index finger and other fingers represent the positive X, y, and Z axes respectively.

The X-axis is pointing to the right, the Y-axis is pointing down, and the Z-axis is pointing towards itself, as shown below (the grid plane corresponds to the screen).

transform

The core of CSS3 3D transformation is the transform property. The Transform property applies to 2D or 3D transformations of elements and allows you to rotate, scale, move, tilt, etc. Grammar: transform: none | transform – functions provides;

3D-related transform-functions.

  • translate3d(x,y,z): translation.
translateX(x)
translateY(y)
translateZ(z)
Copy the code
  • scale3d(x,y,z): zoom.
scaleX(x)
scaleY(y)
scaleZ(z)
Copy the code
  • rotate3d(x,y,z,angle): rotation.
rotateX(angle)
rotateY(angle)
rotateZ(angle)
Copy the code

The conversion values of the three directions of XYZ can be set together or separately. Translation and scaling are easy to understand, but here we’re going to focus on rotation.

Rotate3d (x,y,z, Angle) is not usually used, more use rotateX(Angle) rotateY(Angle) rotateZ(Angle) this direction component way.

  • RotateX (): corresponds to rotation around the X-axis. The passed parameter is rotateX(180deg), which means rotation around the X-axis by 180 degrees. How do I rotate it? I’m going to refer to the coordinate system up here. Again, rightExpand the left hand and point the thumb in the positive direction of the X axis. The bending direction of the other fingers is the positive direction of rotation. The same is true for the rotation direction of the Y axis and z axis. sorotateX(>0deg)It’s going to flip inward, if thetarotateX(<0deg)It just flips out. Below isrotateX(180deg).

  • RotateY (): corresponds to rotation around the Y-axis, as shown in the figure belowrotateY(180deg).

  • RotateZ (): corresponds to rotation around the z-axis, as shown in the figure belowrotateZ(180deg).

transform-style

Then, we can write a simple card flip effect.

The card has two sides, which are represented by two divs. “stack” the two divs together using position: Absolute. Then rotate one of the sides to the back to have two sides.

<div class="card"> <! <div class="card-face card-front"> </div> <! - the card the opposite - > < div class = "card - the face card - back" > opposite < / div > < / div >Copy the code
.card{
    width: 200px;
    height: 200px;
    position: relative;
    margin: 30px auto;
    transition: transform 2s;    
}
.card-face {
    width: 100%;
    height: 100%;
    position: absolute;
}
.card-front {
    background: cornflowerblue;
}
.card-back {
    background: rgb(248, 167, 140);
    transform: rotateY( 180deg );
}
.card:hover {
    transform: rotateY( 180deg );
}
Copy the code

Open the browser, the reverse is flipped, but it did not turn to the back, but in front of the plane, and hover not hover how to flip also can not see the front. In 2 d planeposition:absoluteThe back div covered the front div and rotated in 3D space. Both divs should have had a chance to show up, but they didn’t.

This is where the transform-style property comes in. The transform-style property is an important property in 3D space, specifying how nested elements are rendered in 3D space. It has two property values: Flat and preserve-3D. Flat is the default value, indicating that all child elements are rendered in 2D plane. Preserve-3d indicates that all child elements are rendered in 3d space. We add the transform-style property:

.card{
    width: 200px;
    height: 200px;
    position: relative;
    margin: 30px auto;
    transform-style: preserve-3d;
    transition: transform 2s;    
}
Copy the code

So, there is a card flip effect ~~

Transform-style:

This property cannot be inherited, so whenever a child element needs to be 3D rendered, it must be added to the parent element. In the following book flipping example, class=”card” elements must be set to transform-style, not just class=”demo” elements to be inherited by class=”card” elements, that won’t work.

If transform-style is set to preserve-3d, the overflow value cannot be set to hidden, otherwise it will have the same effect as transform-style: flat.

transform-origin

Transform-origin allows you to change the origin of an element’s deformation. The default value is 50%, 50%, and 0, which means that the rotation, shift, and scaling of an element is based on its central position in the X and y directions (z-axis is at 0, the plane of the screen). If transform-Origin is not set, the above flip effect is flipped around the vertical center line of the card. We can set transform-Origin to change the transform center to achieve more cool effects.

Transform-origin Possible value, refer to MDN.

Based on the above card flipping, to achieve a book flipping effect.

<div class="card"> <div class="card-face card-front">This is page4! </div> <div class="card-face card-back cover-back"></div> </div> <div class="card"> <div class="card-face card-front">This is page2! </div> <div class="card-face card-back">This is page3! </div> </div> <div class="card"> <div class="card-face card-front cover-front">Welcome to CSS3</div> <div class="card-face card-back">This is page1! </div> </div> </div>Copy the code
//css <style> .demo { width: 400px; height: 300px; perspective: 1200px; /* Change the perspective */ position: relative; margin: 30px auto; } .card{ width: 50%; height: 100%; position: absolute; left: 50%; transform-style: preserve-3d; transform-origin: left; /* Set the origin of the element deformation to the left edge */ display: inline-block; border-left: 1px solid #eee; box-shadow: 4px 4px 2px #eee; transition: transform 2s; } .card .card-face{ width: 100%; height: 100%; box-sizing: border-box; position: absolute; margin: 0; padding-top: 40%; background-color: beige; text-align: center; color: #666; } .card-back { transform: rotateY( -180deg ); } .cover-back{ background: url('./images/pic6.jpg') no-repeat 50% 0; */ background-size: cover; */ background-size: cover; } .cover-front{ background: url('./images/pic6.jpg') no-repeat 0 0; background-size: cover; } /* Flip action */.card.flipped {transform: rotateY(-180deg); } </style>Copy the code

Effect without depth of field:

/ / js < script SRC = "https://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js" > < / script > < script > let zIndex = 1 $('.card').on('click',function(e){// set the z-index to the top of the page. $(e.currenttarget).toggleclass ('flipped').css('z-index', zIndex++)}) </script>Copy the code

perspective

In the example of book flipping, perspective is used to make book flipping more three-dimensional.

The perspective specifies the distance, in px, between the observer and the z=0 plane. Makes an element with a 3d position shift perspective.

For example, perspective: 1200px means that the human eye views the object 1200px away from the screen. The effect that the object should present at this time is the effect that the human eye sees at 1200px distance.

The principle of perspective can refer to the up master, the principle of perspective

When the Perspective attribute is defined for an element, its children get perspective, not the element itself. So the perspective property in the example above is set on the element class=”demo”.

Adjust theperspectiveEffects of different values:The demo code:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, < span style> < span style>. Demo {width: 300px; height: 300px; perspective: 1000px; position: relative; margin: 30px auto; } .cube { width: 100%; height: 100%; position: absolute; transform-style: preserve-3d; animation: rotating ease 18s infinite alternate; } .cube .cube-face { border: 2px solid #000; width: 100%; height: 100%; position: absolute; overflow: hidden; Opacity: 0.5; /* backface-visibility: hidden; */ } .cube-face.is-front { transform: translateZ( 150px ); } .cube-face.is-back { transform: rotateX( -180deg ) translateZ( 150px ); } .cube-face.is-right { transform: rotateY( 90deg ) translateZ( 150px ); } .cube-face.is-left { transform: rotateY( -90deg ) translateZ( 150px ); } .cube-face.is-top { transform: rotateX( 90deg ) translateZ( 150px ); } .cube-face.is-bottom { transform: rotateX( -90deg ) translateZ( 150px ); } .cube-img { display: block; width: 100%; height: 100%; } img{ height: 100%; width: 100%; } @keyframes rotating { /* show-front */ 0%, 100% { transform: translateZ( -150px ); } /* show-back */ 16.5% {transform: translateZ(-150px) rotateX(-180deg); } /* show-left */ 33% { transform: translateZ( -150px ) rotateY( 90deg ); } /* show-right */ 50% {transform: translateZ(-150px) rotateY(-90deg); } /* show-top */ 66% { transform: translateZ( -150px ) rotateX( -90deg ); } /* show-bottom */ 82.5% {transform: translateZ(-150px) rotateX(90deg); } } </style> </head> <body> <div class="demo"> <div class="cube"> <div class="cube-face is-front"><img src="./images/pic1.jpg" /></div> <div class="cube-face is-back"><img src="./images/pic2.jpg" /></div> <div class="cube-face is-right"><img src="./images/pic3.jpg" /></div> <div class="cube-face is-left"><img src="./images/pic4.jpg" /></div> <div class="cube-face is-top"><img src="./images/pic5.jpg" /></div> <div class="cube-face is-bottom"><img src="./images/pic6.jpg" /></div> </div> </div> </body> </html>Copy the code

Complete a card mini-game

In order to practice CSS3 3D transformation, let’s write a small game. Memory flip card. The result is the large cover image on the top. Click the card to flip, if the pattern is the same as last time, even if the match is successful, the whole panel of pictures are flipped over, even if the game is finished. Let’s write this game in vUE.

Shuffling thinking

The translate attribute is used for card positioning, so shuffling changes the translate attribute of the element.

InitImage (){// Initialize card array const NUM = 8 let firstAdd = true let I = 0 while(I < NUM){this.icons. Push ({url: Id: 'PIC ${I}${firstAdd}', flip: false, loc: null}) if(! // For the same pattern, add firstAdd =! FirstAdd}}, initLocation(){// Generate an array to initialize the translate attribute x,y const LATTICE_NUM = 4, delta = 150, gap = 10 for(let I =0; i<LATTICE_NUM; i++){ for(let j=0; j<LATTICE_NUM; j++){ this.locs.push({ x: j * ( gap + delta ), y: I * (gap + delta)})}}, shuffle(){this.locs.sort(() => {// Shuffle (){this.locs.sort() => { Return.5 - math.random (); }); this.icons.forEach((item,index) => { item.flip = false item.loc = this.locs[index] }) },Copy the code

Flip thought

Flip card idea is based on the above card flip demo.

<div class="board"> <div v-for="item in icons" :key="item.id" @click="check(item)" class="card" :style="{ transform: `translate(${item.loc.x}px, ${item.loc.y}px) rotateY( ${item.flip? 180:0}deg )` }"> <! - the card positive -- > < div class = "card - the face card - front" > < img: SRC = "item. The url" / > < / div > <! - the card the opposite - > < div class = "card - the face card - back" > < img SRC = ". / img/back. PNG "/ > < / div > < / div > < / div >Copy the code
.card-front { transform: rotateY( 180deg ); /* * * * * * * * * * * * * * * * *Copy the code
check(item){ item.flip = ! Item.flip // flip card}Copy the code

The complete code for the game:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, "> <title>Memory Game</title> <style>. margin: 0 auto; padding-bottom: 50px; border: 1px solid green; text-align: center; background: #243B55; color: #ddd; } .reset{ text-align: right; margin: 30px 120px 50px; } .reset span{ background-color: #FD746C; padding: 8px 30px 10px; cursor: pointer; border-radius: 4px; font-size: 20px; } .board{ width: 600px; height: 600px; position: relative; margin: 30px auto; } .card{ background-color: #fff; width: 120px; height: 120px; position: absolute; transform-style: preserve-3d; transition: transform 1s; } .card img{ width: 70%; margin-top: 10%; } .card-face{ width: 100%; height: 100%; position: absolute; overflow: hidden; backface-visibility: hidden; } .card-front { transform: rotateY( 180deg ); } </style> </head> <body> <div class="game"> <h2> Welcome to our memory game! </h2> <div id="root"> <div class="reset"> <span @click="initStatus">reset</span> </div> <div class="board"> <div v-for="item in icons" :key="item.id" @click="check(item)" class="card" :style="{ transform: `translate(${item.loc.x}px, ${item.loc.y}px) rotateY( ${item.flip? 180:0}deg )` }" > <! - the card positive -- > < div class = "card - the face card - front" > < img: SRC = "item. The url" / > < / div > <! Reverse - card - > < div class = "card - the face card - back" > < img SRC = ". / img/back. PNG "/ > < / div > < / div > < / div > < / div > < / div > < / body > <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> new Vue({ el: "#root", data: { icons: [], locs: [], current: null }, methods: { init(){ this.initImage() this.initLocation() this.initStatus() }, shuffle(){ this.locs.sort( () => { return .5 - Math.random(); }); this.icons.forEach((item,index) => { item.flip = false item.loc = this.locs[index] }) }, initImage(){ const NUM = 8 let firstAdd = true let i = 0 while(i < NUM){ this.icons.push({ url: `./img/pic${i}.png`, value: i, id: `pic${i}${firstAdd}`, flip: false, loc: null }) if(! firstAdd) i++ firstAdd = ! firstAdd } }, initLocation(){ const LATTICE_NUM = 4, delta = 150, gap = 10 for(let i=0; i<LATTICE_NUM; i++){ for(let j=0; j<LATTICE_NUM; j++){ this.locs.push({ x: j * ( gap + delta ), y: i * ( gap + delta ) }) } } }, CalcTime (){if(this.starttime === -1) this.starttime = new Date().gettime () // record the game startTime if(! this.icons.some((icon) => { return icon.flip === false })){ let endTime = new Date().getTime() let time = (endtime-this.startTime)/1000 this.timer = setTimeout(() => {// Animation complete time 1s, 1s after the alert(' you use ${time.tofixed (0)} seconds to complete the game! `) clearTimeout(this.timer) },1000) } }, initStatus(){ this.timer = null this.animation = false this.startTime = -1 this.shuffle() }, check(item){ if(this.animation || item.flip) return item.flip = ! If (this.current === null){this.current = item return} If (this.current. Value === item.value){// If (this.current. Value === item.value){ This.animation = true this.timer = setTimeout(() => {item.flip =! item.flip this.current.flip = ! this.current.flip this.current = null this.animation = false clearTimeout(this.timer) },600) } } }, created(){ this.init() } }) </script> </html>Copy the code