Writing in the front
The Omi framework was officially released → Omi-Transform.
Made css3 transform super easy. Made 60 FPS easy.
As the Omi component-based development special effects motion solution, you can quickly and easily support the CSS3 Transform setup in Omi projects. Css3transform has experienced the baptism of a large number of projects. As a mobile Web special effects solution, it is widely used in wechat, hand-Q interest tribes, suntrace, QQ group, QQ nearby and other projects. At the cost of radical modification of DOM attributes, it brings extremely convenient programmability.
You can get a quick look at the cSS3Transform homepage.
You can also use css3Transform declaratively in React:
render() {
return (
<Transform
translateX={100}
scaleX=0.5} {
originX=0.5} {>
<div>You want to move the DOM</div>
</Transform>)}Copy the code
That’s not the point. It’s the omi-transform.
Master omi-transform in 3 minutes
Install via NPM
npm install omi-transform
Copy the code
use
import { render, WeElement, define } from "omi"
import "omi-transform"
define("my-app".class extends WeElement {
static observe = true
install() {
this.data.rotateZ = 30
this.linkRef = (e) = > {
this.animDiv = e
}
}
installed() {
setInterval((a)= > {
//slow
//this.data.rotateZ += 2
//fast
this.animDiv.rotateZ += 2
//sync for update call of any scenario
this.data.rotateZ = this.animDiv.rotateZ
}, 16)
}
render(props, data) {
return (
<css3-transform rotateZ={data.rotateZ} translateX={0} perspective={0} >
<div ref={this.linkRef}>
omi-transform
</div>
</css3-transform>
)
}
})
render(<my-app />, "body")
Copy the code
- Use the DOM that needs movement
<css3-transform></css3-transform>
The parcel - Tag the DOM that needs to use cSS3Transform
ref
Used to manipulate the DOM directly - You can use this.refs.animDiv in component functions to read or set the CSS Transform property
- This.refs. XXX supports “translateX”, “translateY”, “translateZ”, “scaleX”, “scaleY”, “scaleZ”, “rotateX”, “rotateY”, “RotateZ “, “skewX”, “skewY”, “originX”, “originY”, “originZ”,” Perspective “these properties are set and read
- Perspective represents the distance of a perspective projection
A DOM in a component may be updated during movement due to other logic. It could be a user interaction, it could be a callback to a data return. Therefore, it is important to preserve the state of the DOM before and after the update, otherwise there will be flickering, jumping effects, and other display logic errors.
Can you see that the code above does not Diff during DOM motion? Component does not update? What if the component updates and all motion states are lost? How did Omi solve this problem? The code above provides the answer:
Use this.data.rotatez to synchronize the state of the moving DOM to prevent accidental updates.
To demonstrate
Supported properties
Property | Describe |
---|---|
translateX | translateX |
translateY | translateY |
translateZ | translateZ |
scaleX | scaleX |
scaleY | scaleY |
scaleZ | scaleZ |
rotateX | rotateX |
rotateY | rotateY |
rotateZ | rotateZ |
skewX | skewX |
skewY | skewY |
originX | the basic x point of rotation |
originY | the basic y point of rotation |
originZ | the basic z point of rotation |
perspective | Perspective projection distance |
You can get or you can set.
The performance comparison
Because the React version has a diff process, and then the apply diff to DOM process, the state changes don’t replace the entire innerHTML, so it’s still pretty cheap for browsers to render. However, the time of DIff process in JS still needs to be taken by Profiles. If the time is serious, the UI thread will still be stuck without running in Webworker, resulting in lag, animation lag, frame loss, interaction delay, etc. So it’s worth taking a look at the CPU time.
The following data compares omi-Transform and React-Transform, using Chrome Profiles in both ways.
Let’s take a look at the total time comparison:
The react – transform:
Omi – transform:
- React takes approximately 1686ms of CPU time in 8739 seconds
- In Omi mode, the CPU consumption is approximately 700ms in 9254ms seconds
Without profiles, we can imagine that React will be slower, because the state change needs to go through the React life cycle. However, it can be seen that the React time is still within the acceptable range, but not unacceptably slow.
The Omi approach takes exactly the same time as traditional native JS. Because the motion process does not perform DOM Diff, directly manipulate DOM!!
Omi self comparison
//slow
this.data.rotateZ += 2
Copy the code
//fast
this.animDiv.rotateZ += 2
this.data.rotateZ = this.animDiv.rotateZ
Copy the code
Compare the execution efficiency of the above two code blocks. Open Google Browser Performance and run for about 10 seconds. Open Summary comparison:
Slow | Fast |
---|---|
You can see that both omi methods have very high performance, a lot of idle time in 10 seconds, but FAST is really fast, and scripting takes much less time. But why not? Because the DOM structure is simple, if the DOM structure is more complex, fast’s direct manipulation of the DOM will beat slow’s way! Let’s verify:
Render’s DOM structure has been modified to be complex:
Open Google Chrome Performance run for about 10 seconds and open Summary comparison:
Slow | Fast |
---|---|
You can see that Scripting Time has opened up a gap!
Data before and after comparison:
DOM structure | Slow | Fast |
---|---|---|
simple | ||
complex |
It can be seen that there is no big difference between the two times of Fast and the two times of Slow. So what is the principle of Fast kernel CSS3Transform?
css3transform
The installation
npm install css3transform
Copy the code
API
Transform(domElement, [notPerspective])
Copy the code
By calling the above line of code, To set or read domElement’s “translateX”, “translateY”, “translateZ”, “scaleX”, “scaleY”, “scaleZ”, “rotateX”, “rotateY”, RotateZ, skewX, skewY, originX, originY, originZ!
The road is simple.
Use the pose
Transform(domElement)//or Transform(domElement, true);
//set
domElement.translateX = 100
domElement.scaleX = 0.5
domElement.originX = 50
//get
console.log(domElement.translateX)
Copy the code
Traditional CSS3 programming problems
Previously, we used animate. CSS, zepto/jQuery animate method, or Tween.js + CSS3 for interactive effects programming. There are three disadvantages:
- Is not intuitive
- Not directly
- Is not convenient
Is not intuitive
Take a look at the chart below:
The order affects the result, which is not intuitive. So why is that? You can compare the final matrix with new WebKitCSSMatrix(transform_str).
This also directly shows that the matrix does not obey the commutative law. A*B ! = B*A
Not directly
Zepto pose:
$("#some_element").animate({
opacity: 0.25.left: '50px'.color: '#abcdef'.rotateZ: '45deg'.translate3d: '0, 10 px, 0'
}, 500.'ease-out')
Copy the code
Translate3d: ‘0,10px,0’ Not to mention programming with some library of motion or time. You might argue that ‘ease-out’ doesn’t ease? But if I need x and y and z to correspond to different easing functions, this string-based programming would be a bit tricky. Because it ends up being a string assigned to the DOM element.
Tween. Js posture
var position = { x: 100.y: 100.rotation: 0 },
target = document.getElementById('target')
new TWEEN.Tween(position)
.to({ x: 700.y: 200.rotation: 359 }, 2000)
.delay(1000)
.easing(TWEEN.Easing.Elastic.InOut)
.onUpdate(function update() {
var t_str= 'translateX(' + position.x + 'px) translateY(' + position.y + 'px) rotate(' + Math.floor(position.rotation) + 'deg)'
element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = t_str
});
Copy the code
Using strings is tiring to watch. Not to mention the torture of writing it.
The animate. CSS position:
@keyframes pulse {
from {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1); 50%} {-webkit-transform: scale3d(1.05, 1.05, 1.05);
transform: scale3d(1.05, 1.05, 1.05);
}
to {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1); }}Copy the code
Animate. CSS encapsulates a bunch of keyframe animations, and developers only need to care about adding or removing the relevant animation classes. This gives interactive effects a great deal of traversal, but with a pinch:
- Not programmable enough
- Applicable to simple scenarios
- only
end
Callbacks, nochange
The callback
Is not convenient
The rotation point reference point of the transform is in the center by default, but sometimes the system is not in the center. We traditionally use transform-Origin to set the reference point.
Note that transform-origin is another property, not transform. But what if you want to sport transform-Origin? Does this design fail? Is there a scene where origin needs to be moved? This is often used in game design and will be discussed in a separate article later. The fact is, there are scenarios that need to move origin to achieve some effect.
summary
Based on the above inconvenience, so there is a CSS3Transform!
- Css3transform focuses on CSS3 Transform reading and setting an ultra-lightweight JS library, greatly improving the programmability of CSS3 Transform
- Css3transform is highly abstract and does not bind to any time or motion frame, so it can be easily paired with any time or motion frame
- Css3transform uses Matrix3D as the final output to dom objects, hardware acceleration without loss of programmability
- Css3transform has super easy-to-use API, easy to get started in one minute, embedded in real project in two minutes
- Css3transform extends the power of Transform itself, making Transform Origin more convenient
In actual combat
You can easily create this swing effect with CreateJS ‘Tweenjs:
var element = document.querySelector("#test")
Transform(element)
element.originY = 100
element.skewX = - 20
var Tween = createjs.Tween,
sineInOutEase = createjs.Ease.sineInOut
Tween.get(element, {loop: true}).to({scaleY: 8.}, 450, sineInOutEase).to({scaleY: 1}, 450, sineInOutEase)
Tween.get(element, {loop: true}).to({skewX: 20}, 900, sineInOutEase).to({skewX: - 20}, 900, sineInOutEase)
Copy the code
The above code is minimal. Here’s a little explanation:
- The initial skewX for the element is -20, in keeping with scale
- The originY of the element is 100, with penguin’s Bottom Center as the base point
As you can see, because cSS3Transform is highly abstract, it can be easily paired with Tweenjs without any stress.
The principle of
Css3transform can mix not only CSS3 transforms into DOM elements, but also into any object literal. You can also use css3Transform as a tool, which provides some basic mathematical capabilities.
It is important to note here that the previous positions can continue to be used, but here are three other positions to use.
Grammar 1
Transform(obj, [notPerspective]);
Copy the code
As you can see, nothing else needs to change. However, the first parameter can be passed not only to DOM elements, but also to any object literal, etc.
No pun intended. Look at the posture first
var element = document.querySelector("#test"),
obj = {}
Transform(obj)
obj.rotateZ = 90
element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform
Copy the code
See, you can pass in not only DOM elements, but also object literals. You can print out obj.transform, which is 90 degrees, so it generates the matrix:
perspective(500px) matrix3d(0.1.0.0.- 1.0.0.0.0.0.1.0.0.0.0.1)
Copy the code
You can also turn off perspective projections, for example:
var element = document.querySelector("#test"),
obj = {}
// Turn off perspective projection
Transform(obj, true)
obj.rotateZ = 90
element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform
Copy the code
The generated matrix is:
matrix3d(0.1.0.0.- 1.0.0.0.0.0.1.0.0.0.0.1)
Copy the code
What about the posture of movement? Here’s an example with tween.js:
var element = document.querySelector("#test"),
obj = { translateX: 0.translateY: 0 }
Transform(obj);
var tween = new TWEEN.Tween(obj)
.to({ translateX: 100.translateY: 100 }, 1000)
.onUpdate(function () {
element.style.transform = element.style.msTransform = element.style.OTransform = element.style.MozTransform = element.style.webkitTransform = obj.transform
})
.start()
requestAnimationFrame(animate)
function animate(time) {
requestAnimationFrame(animate)
TWEEN.update(time)
}
Copy the code
So what about the traditional pose?
var element = document.querySelector("#test")
Transform(element)
var tween = new TWEEN.Tween({ translateX: element.translateX, translateY: element.translateY })
.to({ translateX: 100.translateY: 100 }, 1000)
.onUpdate(function () {
element.translateX = this.translateX
element.translateY = this.translateY
})
.start()
requestAnimationFrame(animate)
function animate(time) {
requestAnimationFrame(animate)
TWEEN.update(time)
}
Copy the code
TWEEN. TWEEN iterates through all properties and sets the initial value, as shown in the code inside TWEEN:
// Set all starting values present on the target object
for (var field in object) {
_valuesStart[field] = parseFloat(object[field], 10)}Copy the code
So you can’t just say new tween.tween (element). Because before start, the program can completely collect all the attributes that need to be moved. We can encapsulate a Tween ourselves to support this simple approach. Such as:
var Tween = function (obj) {
this.obj = obj
return this
}
Tween.prototype = {
to: function (targets, duration, easing) {
this.duration = duration
this.targets = targets
return this
},
start: function () {
this.startTime = new Date(a)this._beginTick()
},
_beginTick: function () {
var _startValues = {},
targets = this.targets
for (var key in targets) {
if (targets.hasOwnProperty(key)) {
_startValues[key] = this.obj[key]
}
}
var self = this
this._interval = setInterval(function () {
var dt = new Date() - self.startTime
for (var key in targets) {
if (targets.hasOwnProperty(key)) {
if (dt >= self.duration) {
clearInterval(self._interval)
} else {
var p = dt / self.duration;
var dv = targets[key] - self.obj[key]
self.obj[key] += dv * p
}
}
}
}, 15)}}Copy the code
To make it easy to use setInterval to loop, you can use another method. Now you can use the following:
var element = document.querySelector("#test")
Transform(element)
var tween = new Tween(element)
.to({ translateX: 100.translateY: 100 }, 1000)
.start();
Copy the code
Of course this is a bit off topic. This is just the difference between mounting directly using the DOM versus using a third-party object. Third party hangers contain points to beat the cow on the hill feel. Of course.. It’s not over yet. It’s not just that. You can also use cSS3Transform entirely as a computing tool.
Syntax 2
Transform.getMatrix3D(option)
Copy the code
posture
var matrix3d = Transform.getMatrix3D({
translateX: 0.translateY: 100.scaleX:2
})
console.log(matrix3d)
Copy the code
Print it out and you’ll get the following values:
You can do whatever you want with this value. Transform.getmatrix3d: transform.getMatrix3d: transform.getMatrix3d: transform.getMatrix3d
Transform.getMatrix3D = function (option) {
var defaultOption = {
translateX: 0.translateY: 0.translateZ: 0.rotateX: 0.rotateY: 0.rotateZ: 0.skewX: 0.skewY: 0.originX: 0.originY: 0.originZ: 0.scaleX: 1.scaleY: 1.scaleZ: 1
};
for (var key inoption) { ... . . }Copy the code
Grammar 3
Transform.getMatrix2D(option)
Copy the code
Not only 3D matrix, cSS3Transform also provides 2D tool function support.
posture
var matrix2d = Transform.getMatrix2D({
translateX: 0.translateY: 100.scaleX:2
});
console.log(matrix2d);
Copy the code
Print it out and you’ll get the following values:
- A Horizontal scaling
- B Horizontal stretching
- C Vertical stretching
- D Vertical scaling
- Tx horizontal displacement
- Ty vertical displacement
So what’s the use of getting this Matrix2D?
- Scale: scale(sx, sy) equals matrix(sx, 0, 0, sy, 0, 0);
- Translate: Translate (tx, ty) equals matrix(1, 0, 0, 1, tx, ty);
- Rotate (deg) equals matrix(cos(deg), sin(deg), -sin(deg), cos(deg), 0, 0); rotate(deg) equals matrix(cos(deg), sin(deg), -sin(deg), cos(deg), 0, 0);
- Tension: Skew (Degx, degy) is equal to matrix(1, Tan (deGY), Tan (degx), 1, 0, 0);
Transform.getMatrix2D: Transform. GetMatrix2D: Transform.
Transform.getMatrix2D = function(option){
var defaultOption = {
translateX: 0.translateY: 0.rotation: 0.skewX: 0.skewY: 0.originX: 0.originY: 0.scaleX: 1.scaleY: 1}; . . . }Copy the code
Special Attention
Transform.getMatrix2D and transform. getMatrix3D both support origin, Say goodbye to transform-Origin for transform. getMatrix2D and transform. getMatrix3D shew uses half of rotation instead of the traditional Math
For example, 2d skew:
Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX)
Copy the code
Why do I use half of rotation instead of Math.tan? The reason for this is simple: Math.tan is extremely hard to distort, and there are infinite values that distort across the screen.
Half of rotation doesn’t.
Does getMatrix2D work?
When used with Dom Transformations, it can be used with browsers that do not support CSS3 3D Transforms
For example, we can easily convert some transformation attributes to CSS3 attributes to DOM:
var matrix = Transform.getMatrix2D({
rotation: 30.scaleX: 0.5.scaleY: 0.5.translateX: 100
});
ele.style.transform = ele.style.msTransform = ele.style.OTransform = ele.style.MozTransform = ele.style.webkitTransform = "matrix(" + [matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty].join(",") + ")"
Copy the code
Used for Canvas and SVG Transformation
What? Canvas and SVG? Yes, for example, draw an image on Canvas rotated 30 degrees, shrunk to 0.5 times, and shifted (200,200) :
var canvas = document.getElementById("ourCanvas"),
ctx = canvas.getContext("2d"),
img = new Image(),
rotation = 30 * Math.PI / 180
img.onload = function () {
ctx.sava();
ctx.setTransform(
0.5 * Math.cos(rotation), 0.5 * Math.sin(rotation),
0.5 * Math.sin(rotation), 0.5 * Math.cos(rotation),
200.200
)
ctx.drawImage(img, 0.0)
ctx.restore()
};
img.src = "asset/img/test.png"
Copy the code
This is our traditional pose. After using transform. getMatrix2D, it looks like this:
var canvas = document.getElementById("ourCanvas"),
ctx = canvas.getContext("2d"),
img = new Image()
var matrix = Transform.getMatrix2D({
rotation: 30.scaleX: 0.5.scaleY: 0.5.translateX: 200.translateY: 200
});
img.onload = function () {
ctx.sava();
ctx.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
ctx.drawImage(img, 0.0);
ctx.restore();
}
img.src = "asset/img/test.png"
Copy the code
As you can see, this saves developers from having to piece together the matrix themselves. SVG particles will not be used as an example, similar to the DOM example, which you will be able to solve quickly.
Star & Fork
– > omi – transform