Writing in the front

Thanks to the efforts of Tencent Omi team, today you can use Cax engine to render SVG in small programs with high performance!

SVG is a graphical format for describing two-dimensional Vector Graphics based on extensible Markup Language (EXTENSIBLE Markup Language). It was developed by the World Wide Web Consortium and is an open standard. The advantages of SVG are many:

  • SVG uses an XML format to define graphics that can be created and modified through a text editor
  • SVG images can be searched, indexed, scripted, or compressed
  • SVG is scalable and enlarges images without degrading quality
  • SVG images can be printed at any resolution with high quality
  • SVG can be read and modified by a large number of tools (such as Notepad)
  • SVG is smaller in size and more compressible and programmable than JPEG and GIF images
  • SVG fully supports DOM programming and is interactive and dynamic

To support these excellent features, you need to support the SVG tag. For example, write directly in a small program:

<svg width="300" height="150">
  <rect
    bindtap="tapHandler" height="100" width="100"
    style="stroke:#ff0000; fill: #0000ff">
  </rect>
</svg>
Copy the code

The above defines the structure, style, and click behavior of SVG. Background-image: URL (“data:image/ SVG + XML……. “) , or base64 as a background-image URL.

So what to do? Is there a way to make small programs support SVG? The answer is yes! The following things are needed (to stand on the shoulders of giants) :

  • JSX, the most powerful UI expression in history, supports writing XML-HyperScript interchange in JS language
  • Small program built-in Canvas renderer
  • Cax latest rendering engine
  • HTM, Hyperscript Tagged Markup, may be an alternative to JSX or an alternative, using the ES standard template syntax for Hyperscript runtime/compile-time generation, created by the Preact authors (also Google engineers)

Here’s a little explanation of Hyperscript:

Like in JSX

<div>
  Hello {this.props.name}
</div>
Copy the code

Or HTM in JS:

html`
      
Hello {this.props.name}
`
Copy the code

It will be compiled as:

h(
  "div".null."Hello ".this.props.name
);
Copy the code

Nested divs will also compile to h nested H, for example

<div>
  <div>abc</div>
</div>
Copy the code

The compiled:

h(
  "div".null,
  h(
    "div".null."abc"))Copy the code

The definition of the h function is also fairly concise:

function h(type, props, ... children) {
  return { type, props, children }
}
Copy the code

Through the execution of H, structured data can be obtained from JS, which is the so-called virtual DOM. It is important to note that HTM has a slight runtime overhead that JSX does not.

In one sentence:

Use the small program’s built-in Canvas renderer to implement a subset of the SVG standard in Cax, using JSX or HTM to describe the behavior of SVG structures

Look directly at the small program type use case:

import { html, renderSVG } from '.. /.. /cax/cax'

Page({
  onLoad: function () {

    renderSVG(html` 
      `.'svg-a'.this)},tapHandler: function () {
    console.log('You clicked rect')}})Copy the code

Where SVG-a corresponds to the CAX-Element ID in WXML:

<view class="container">
  <cax-element id="svg-c"></cax-element>
</view>
Copy the code

Declaring component Dependencies

{
  "usingComponents": {
    "cax-element":".. /.. /cax/index"}}Copy the code

Small program display effect:

You can set the bounds of bound events using width, height, bound-x, and bound-y, for example:

<path width="100" height="100" bounds-x="50" bounds-y="50" />
Copy the code

Note that an element’s event-triggered bounding box is affected by its own or its parent’s transform, so it is not an absolute coordinate rect triggering region.

To take another complex example, draw Omi’s logo in SVG:

renderSVG(html
      
         < / > g < / SVG > `
       .'svg-a'.this)
Copy the code

Small program display effect:

Using CAX to render SVG in OMIP and MPS, you can do so without using HTM. For example, the above two examples are implemented in OMIP:

    renderSVG(
<svg width="300" height="220">
  <rect bindtap="tapHandler"
  height="110" width="110"
  style="stroke:#ff0000; fill: #ccccff"
  transform="translate(100 50) rotate(45 50 50)">
  </rect>
</svg>.'svg-a'.this.$scope)
Copy the code
<g transform="translate(50,10) scale(0.2 0.2)"> <circle fill="#07C160" /> <polygon fill="white" points="159.97,807.8 338.71,532.42 509.9,829.62 519.41,829.62 /> < span style =" font-size: 14px;" Cx = "839.36" cy = "242.47" r = "50" / > < / a > g < / SVG >, 'SVG -a, enclosing $scope)Copy the code

Note that the last parameter passed in omip is not this, but this.$scope.

In MPS, to be more thorough, you can create SVG files separately and import them via import.

// Note that test.svg cannot be written here because MPS will compile test.svg to test.js
import testSVG from '.. /.. /svg/test'
import { renderSVG } from '.. /.. /cax/cax'

Page({
  tapHandler: function(){
    this.pause = !this.pause
  },
  onLoad: function () {
    renderSVG(testSVG, 'svg-a'.this)}})Copy the code

Such as the test. The SVG:

<svg width="300" height="300">
  <rect bindtap="tapHandler" x="0" y="0" height="110" width="110"
         style="stroke:#ff0000; fill: #0000ff" />
</svg>
Copy the code

Will be compiled by MPS to:

const h = (type, props, ... children) = > ({ type, props, children });
export default h(
  "svg",
  { width: "300".height: "300" },
  h("rect", {
    bindtap: "tapHandler".x: "0".y: "0".height: "110".width: "110".style: "stroke:#ff0000; fill: #0000ff"}));Copy the code

So to summarize:

  • You can use SVG directly in MPS using import SVG files
  • You can use JSX’s SVG directly in OMIP
  • You can use SVG directly in native applets using HTM

That’s it? Far from it. Look at this example of CAx in a small program:

Detailed code:

renderSVG(html< span style =" box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; 283,88 285,78.7 286,69.3 288,60 289,61.3 290,62.7 291,64 291,64 297,63 300,63 303,63 309,64 309,64 31,62.7 31,61.3 312,60 314,69.3 315,78.7 317,88 365,82 380,56 366, 18 431,71 453,181 366,262 394,187 356,181 344,213 328,185 309,184 "Style =" box-sizing: border-box! Important; fill: black"> 
        `.'svg-c'.this)
Copy the code

Try the famous SVG tiger:

The path is too long, so I don’t want to post the code, which can be viewed here

Is that all? To be continued… , follow-up supplement:

Pasiton label

import { html, renderSVG } from '.. /.. /cax/cax'

Page({
  onLoad: function () {


    const svg = renderSVG(html`
<svg width="200" height="200">
  <pasition duration="200" bindtap=The ${this.changePath}width="100" height="100" C1.172 from = "M28.228, 23.986 L47.092, 5.122-1.171, 1.172-3.071, 0-4.242 - c - 1.172-1.172-3.07-1.172-4.242, 0 l23. 986,19.744 L5.121, 0. 88 C - 1.172-1.172-3.07-1.172-4.242, 0 c - 1.172, 1.171-1.172, 3.071, 0,4.242 l18.865, L0.879 18.864, 42.85-1.172 c, 1.171-1.172, 3.071, 0, 4 242. C1.465, 47.677, 2.233, 47.97, 3,47.97 s1.535-0.293, 2.121, 0.879 l18.865 18.864 L42.85, c0.586 47.091, 0.586, 1.354, 0.879, 2.121, 0.879 S1535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z" to="M49.1 23.5h2.1c0.9 23.3.5 0 24.50 25.6s0.9 2.1 2.1 2.1 H47C1. 1 0 2.1-0.9 2.1-2.1C51.2 24.5 50.3 23.5 49.1 23.5zM49.1 7.8 H2.1c0.9 7.8 0 8.8 0 9.9 C0 1.1 0.9 2.1 2.1 2.1H47C1.10 0 2.1-0.9 2.1-2.1C51.2 8.8 50.3 7.8 49.1 7.8zM49.1 39.2H2.1c0.9 39.2 0 40.1 0 41.3S0.9 2.1 2.1 2.1 H47C1.10 0 2.1-0.9 2.1-2.1S50.3 39.2 49.1 39.2z" from-stroke="red" to-stroke="green" from-fill="blue" to-fill="red" stroke-width="2" /> </svg>`.'svg-c'.this)

    this.pasitionElement = svg.children[0]},changePath: function () {
    this.pasitionElement.toggle()
  }
})
Copy the code

Pasiton provides the ability to switch between the two paths and the color, the most common scenario is the transformation of the path after the menu button and the close button are clicked.

For example, see color and path change at the same time:

Linear motion

Here is an example of using SVG in MPS:

import { renderSVG, To } from '.. /.. /cax/cax'

Page({
  tapHandler: function(){
    this.pause = !this.pause
  },

  onLoad: function () {
    const svg = renderSVG(html` 
      `
    , 'svg-a'.this)
    const rect = svg.children[0]
    rect.originX = rect.width/2
    rect.originY = rect.height/2
    rect.x = svg.stage.width/2
    rect.y = svg.stage.height/2
    this.pause = false
    this.interval = setInterval((a)= >{
      if(!this.pause){
        rect.rotation++
        svg.stage.update()
      }
    },15)})Copy the code

The effect is as follows:

Combination of movement

import { renderSVG, To } from '.. /.. /cax/cax'

Page({
  onLoad: function () {

    const svg = renderSVG(html` 
      `
    ,'svg-a'.this)
    const rect = svg.children[0]
    rect.originX = rect.width/2
    rect.originY = rect.height
    rect.x = svg.stage.width/2
    rect.y = svg.stage.height/2

    var sineInOut = To.easing.sinusoidalInOut
    To.get(rect)
        .to().scaleY(0.8.450, sineInOut).skewX(20.900, sineInOut)
        .wait(900)
        .cycle().start()
    To.get(rect)
        .wait(450)
        .to().scaleY(1.450, sineInOut)
        .wait(900)
        .cycle().start()
    To.get(rect)
        .wait(900)
        .to().scaleY(0.8.450, sineInOut).skewX(- 20.900, sineInOut)
        .cycle()
        .start()
    To.get(rect)
        .wait(1350)
        .to().scaleY(1.450, sineInOut)
        .cycle()
        .start()

      setInterval((a)= > {
          rect.stage.update()
      }, 16)}})Copy the code

The effect is as follows:

other

  • Vscode installs the lit- HTML plugin to make HTML HTMcontentThe highlighted
  • I also hope that the small program SVG provides what functions can open issues tell us, after the evaluation passed, we go to implement!
  • Cax SVG Github
  • Reference documentation