Project details
Github address: Github
Demo Example: Demo
background
In the beginning, when the company was doing some activity pages, it often needed to use the function of sharing thousands of screenshots, and it was not just once or twice, but often appeared in various screenshots. The first time you encounter such a requirement, you will basically go to hand lift canvasAPI to do the rendering function. The steps in this case are roughly as follows:
- Write a string
dom template
The label - Apply colours to a drawing
template
intodom
The label - To capture
dom
Element, drawcanvas
canvas
Apply colours to a drawing pictures
The main problem is poor reusability, followed by performance problems. The interface seen by users may not be consistent with the formal rendered interface, and there may be differences in rendering. Because I mainly use VUE in my work and have some research on the core idea of VUE, VUE achieves the rendering of different ends through VNode, is it possible to achieve the rendering of Canvas through VNode? That is, there is no vnode -> HTML -> canvas, but vnode -> canvas. At the same time, using vUE data driven, to achieve the data driven rendering. I have an idea. Let’s do it.
research
60 FPS on the Mobile Web
Canvas is an immediate mode of rendering and does not store additional rendering information. Canvas benefits from immediate mode, allowing drawing commands to be sent directly to the GPU. But using it to build user interfaces requires a higher level of abstraction. Some simple processes, such as drawing an asynchronously loaded resource to an element, are problematic, such as drawing text on a picture. In HTML, this is easy to implement because of the order in which elements exist and the z-index in CSS. Dom rendering is a reserved mode, which is a declarative API for maintaining a hierarchy of objects drawn to it. The advantage of the reserved pattern API is that it is often easier for your application to build complex scenarios, such as DOM. Often this has a performance cost, requiring extra memory to save scenes and update scenes, which can be slow.
To start!
Canvas rendering is actually an attempt. Since our predecessors have done sufficient practice, we will stand on the shoulders of giants to achieve a data-driven canvas rendering based on VUE. Just do it!
Processing vnode
Those familiar with Vue source code should know that Vue uses the render function, passing the createElement method to construct a Vnode, using publish-subscribe mode to realize the monitoring of data, regenerate vNode. All we need to do is start at the VNode layer. So, we implement a listener function based on the Vue source code and mix it with the Vue instance:
Vue.mixin({
// ...
created() {
if (this.$options.renderCanvas) {
// ...
// Listen for changes in vNode references and re-render
this.$watch(this.updateCanvas, this.noop)
// ...}},methods: {
updateCanvas() {
// Simulate the Vue render function
// Find the renderCanvas method defined in the instance and pass in the createElement method
let vnode = this.$options.renderCanvas.call(this._renderProxy, this.$createElement)
}
})
Copy the code
This way we can happily use it inside the component:
renderCanvas (h) {
return h(...)
}
Copy the code
Canvas element handling
We need to make some additional constraints, such as what the DOM div should correspond to in the canvas, what the text in the DOM should correspond to in the canvas… That is, we can do some constraints like this:
Custom labels | Drawing form | Analogy to the dom |
---|---|---|
view/scrollView/scrollItem | rect | div |
text | text | span |
image | img | img |
Each of these element classes inherits from a Super class, and since they have different presentation styles, they implement their own DRAW methods to do custom presentation.
Drawing object layout mechanism implementation
The most basic way to draw a Canvas layout is to pass in a series of coordinate points and the relevant base width and height for the Canvas element, which may be written in the actual project like this:
renderCanvas(h) {
return h('view', {
style: {
left: 10.top: 10.width: 100.height: 100}})}Copy the code
There are several solutions, one of which is to use CSS-Layout to do management. Css-layout supports the following conversion attributes:
This is just one layer of transformation, which helps us to write canvas with CSS thinking, but if we are not happy with CSS in JS, we can also write a Webpack loader to load external CSS:
const css = require('css')
module.exports = function (source, other) {
let cssAST = css.parse(source)
let parseCss = new ParseCss(cssAST)
parseCss.parse()
this.cacheable();
this.callback(null, parseCss.declareStyle(), other);
};
class ParseCss {
constructor(cssAST) {
this.rules = cssAST.stylesheet.rules
this.targetStyle = {}
}
parse () {
this.rules.forEach((rule) = > {
let selector = rule.selectors[0]
this.targetStyle[selector] = {}
rule.declarations.forEach((dec) = > {
this.targetStyle[selector][dec.property] = this.formatValue(dec.value)
})
})
}
formatValue (string) {
string = string.replace(/"/g.' ').replace(/'/g.' ')
return string.indexOf('px')! = =- 1 ? parseInt(string) : string
}
declareStyle (property) {
return `window.${property || 'vStyle'} = The ${JSON.stringify(this.targetStyle)}`}}Copy the code
This is to convert the CSS file into AST syntax tree, and then convert the syntax tree to the definition form required by canvas. And injected into the component as a variable.
Implement list scrolling
If we have a lot of elements and need to scroll, we have to solve the problem of scrolling elements inside the canvas. Here I chose to use Zynga Scroller to simulate the user’s scrolling method and redraw the canvas based on the scrolling coordinate points it returned.
Refer here for details
Event simulation
For the simulation of CLICK, touch and other DOM events, the scheme we adopt is to detect the click region and find out the lowest element, recursively find the parent element and trigger the corresponding event handler, so as to simulate the bubbling of events.
A detailed implementation can be found here
The last
Canvas drawing page is also an innovative attempt, I hope the research here can inspire you, and welcome your PR. There are also a number of performance optimizations, which I don’t have space to describe, but can also be discussed together if you are interested.
Finally: It’s not meant to completely replace DOM based rendering, which still requires text input, copy/paste, accessibility, and SEO. For these reasons, we can use a combination of canvas and DOM-based rendering.