cax-svg - update readme & sync lib from mps
This commit is contained in:
parent
f3dbfcbb18
commit
2424493588
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
|
@ -1,23 +1,70 @@
|
|||
http://tutorials.jenkov.com/svg/index.html
|
||||
# 今天,小程序正式支持 SVG
|
||||
|
||||
todo:
|
||||
经过腾讯 Omi 团队的努力,今天你可以在小程序中使用 SVG!
|
||||
|
||||
* 自动轮播支持 http://tavmjong.free.fr/blog/wp-content/uploads/BATMAN/batman_logos.svg (间隔时间支持)
|
||||
* debug bounds box
|
||||
* pasition easing function
|
||||
SVG 是可缩放矢量图形(Scalable Vector Graphics),基于可扩展标记语言,用于描述二维矢量图形的一种图形格式。它由万维网联盟制定,是一个开放标准。SVG 的优势有很多:
|
||||
|
||||
* Transformation + tojs
|
||||
* Transformation
|
||||
* boundsX 和 boundsY
|
||||
* text
|
||||
* image
|
||||
* SVG Viewport and View Box
|
||||
* event handler
|
||||
* jsx support(extend omip)
|
||||
* SVG 使用 XML 格式定义图形,可通过文本编辑器来创建和修改
|
||||
* SVG 图像可被搜索、索引、脚本化或压缩
|
||||
* SVG 是可伸缩的,且放大图片质量不下降
|
||||
* SVG 图像可在任何的分辨率下被高质量地打印
|
||||
* SVG 可被非常多的工具读取和修改(比如记事本)
|
||||
* SVG 与 JPEG 和 GIF 图像比起来,尺寸更小,且可压缩性、可编程星更强
|
||||
* SVG 完全支持 DOM 编程,具有交互性和动态性
|
||||
|
||||
vscode 安装 lit-html 使 html`内容` 高亮
|
||||
而支持上面这些优秀特性的前提是 - **需要支持 SVG 标签**。比如在小程序中直接写:
|
||||
|
||||
```html
|
||||
<svg width="300" height="150">
|
||||
<rect
|
||||
bindtap="tapHandler" height="100" width="100"
|
||||
style="stroke:#ff0000; fill: #0000ff">
|
||||
</rect>
|
||||
</svg>
|
||||
```
|
||||
|
||||
上面定义了 SVG 的结构、样式和点击行为。但是小程序目前不支持 SVG 标签,仅仅支持加载 SVG 之后 作为 background-image 进行展示,如 `background-image: url("data:image/svg+xml.......)`,或者 base64 后作为 background-image 的 url。
|
||||
|
||||
那么怎么办呢?有没有办法让小程序支持 SVG? 答案是有的!需要下面这些东西(站在巨人的肩膀上):
|
||||
|
||||
* JSX,史上最强 UI 表达式,支持书写 XML-Hyperscript 互转的 JS 语言
|
||||
* 小程序内置 Canvas 渲染器
|
||||
* [Cax 最新渲染引擎](https://github.com/Tencent/omi/tree/master/packages/cax-svg/cax)
|
||||
* HTM,Hyperscript Tagged Markup,可能是 JSX 的替代品或者另一种选择,使用ES标准的模板语法实现的 Hyperscript 运行时/编译时生成,preact 作者(也是google工程师)打造
|
||||
|
||||
一句话总结:
|
||||
|
||||
> 使用小程序内置的 Canvas 渲染器, 在 Cax 中实现 SVG 标准的子集,使用 JSX 或者 HTM 描述 SVG 结构行为表现
|
||||
|
||||
直接看在小程序种使用案例:
|
||||
|
||||
```js
|
||||
import { html, renderSVG } from '../../cax/svg'
|
||||
|
||||
Page({
|
||||
onLoad: function () {
|
||||
|
||||
renderSVG(html`
|
||||
<svg width="300" height="220">
|
||||
<rect bindtap="tapHandler"
|
||||
height="110" width="110"
|
||||
style="stroke:#ff0000; fill: #ccccff"
|
||||
transform="translate(30) rotate(45 50 50)">
|
||||
</rect>
|
||||
</svg>`, 'svg-a', this)
|
||||
|
||||
},
|
||||
|
||||
tapHandler: function () {
|
||||
console.log('你点击了 rect')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
小程序种显示效果:
|
||||
|
||||
|
||||
import testSVG from 'svg/test'
|
||||
|
||||
renderSVG(testSVG, 'canvas-id', this)
|
||||
在来一个复杂的例子,用 SVG 绘制 Omi 的 logo:
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ function renderSVG(vdom, canvas, scope) {
|
|||
canvas,
|
||||
scope
|
||||
)
|
||||
const svg = new SVG(vdom)
|
||||
const svg = new SVG(vdom, scope)
|
||||
stage.add(svg)
|
||||
stage.update()
|
||||
triggerAddedStage(svg)
|
||||
|
|
|
@ -3,7 +3,7 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function circle(props) {
|
||||
export function circle(props, scope) {
|
||||
const options = Object.assign(
|
||||
{
|
||||
r: 0,
|
||||
|
@ -18,6 +18,6 @@ export function circle(props) {
|
|||
// circle.y = Number(options.cy)
|
||||
|
||||
transform(props, circle, Number(options.cx), Number(options.cy))
|
||||
parseEvent(props, circle)
|
||||
parseEvent(props, circle, scope)
|
||||
return circle
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function ellipse(props) {
|
||||
export function ellipse(props, scope) {
|
||||
const options = Object.assign(
|
||||
{
|
||||
rx: 0,
|
||||
|
@ -22,6 +22,6 @@ export function ellipse(props) {
|
|||
// ellipse.x = Number(options.cx)
|
||||
// ellipse.y = Number(options.cy)
|
||||
transform(props, ellipse, Number(options.cx), Number(options.cy))
|
||||
parseEvent(props, ellipse)
|
||||
parseEvent(props, ellipse, scope)
|
||||
return ellipse
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import Group from '../render/display/group'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function group(props) {
|
||||
export function group(props, scope) {
|
||||
const options = Object.assign(
|
||||
{
|
||||
width: 0,
|
||||
|
@ -18,7 +18,7 @@ export function group(props) {
|
|||
// obj.y = Number(options.y)
|
||||
|
||||
transform(props, obj, Number(options.x), Number(options.y))
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@ import { path } from './path'
|
|||
import { pasition } from './pasition'
|
||||
import { group } from './group'
|
||||
import { animate } from './animate'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
class SVG extends Group {
|
||||
constructor(vdom) {
|
||||
constructor(vdom, scope) {
|
||||
super()
|
||||
this.vdom = vdom
|
||||
|
||||
this.scope = scope
|
||||
if (Object.prototype.toString.call(this.vdom) === '[object Array]') {
|
||||
this.vdom = this.vdom.filter(item => typeof item !== 'string')[0]
|
||||
}
|
||||
|
@ -29,43 +30,43 @@ class SVG extends Group {
|
|||
)
|
||||
|
||||
this.vdom.children && this.vdom.children.forEach(vdomChild => {
|
||||
this.generate(root, vdomChild)
|
||||
this.generate(root, vdomChild, scope)
|
||||
})
|
||||
|
||||
root.x = Number(options.x)
|
||||
root.y = Number(options.y)
|
||||
|
||||
parseEvent(vdom.props, root, scope)
|
||||
this.add(root)
|
||||
}
|
||||
|
||||
generate(parent, vdomChild) {
|
||||
generate(parent, vdomChild, scope) {
|
||||
switch (vdomChild.type) {
|
||||
case 'rect':
|
||||
parent.add(rect(vdomChild.props))
|
||||
parent.add(rect(vdomChild.props, scope))
|
||||
break
|
||||
|
||||
case 'circle':
|
||||
parent.add(circle(vdomChild.props))
|
||||
parent.add(circle(vdomChild.props, scope))
|
||||
break
|
||||
|
||||
case 'ellipse':
|
||||
parent.add(ellipse(vdomChild.props))
|
||||
parent.add(ellipse(vdomChild.props, scope))
|
||||
break
|
||||
|
||||
case 'line':
|
||||
parent.add(line(vdomChild.props))
|
||||
parent.add(line(vdomChild.props, scope))
|
||||
break
|
||||
case 'polyline':
|
||||
parent.add(polyline(vdomChild.props))
|
||||
parent.add(polyline(vdomChild.props, scope))
|
||||
|
||||
break
|
||||
|
||||
case 'polygon':
|
||||
parent.add(polygon(vdomChild.props))
|
||||
parent.add(polygon(vdomChild.props, scope))
|
||||
break
|
||||
|
||||
case 'path':
|
||||
const obj = path(vdomChild.props)
|
||||
const obj = path(vdomChild.props, scope)
|
||||
parent.add(obj)
|
||||
if (
|
||||
vdomChild.children &&
|
||||
|
@ -77,14 +78,14 @@ class SVG extends Group {
|
|||
break
|
||||
|
||||
case 'pasition':
|
||||
parent.add(pasition(vdomChild.props))
|
||||
parent.add(pasition(vdomChild.props, scope))
|
||||
break
|
||||
|
||||
case 'g':
|
||||
const p = group(vdomChild.props)
|
||||
const p = group(vdomChild.props, scope)
|
||||
parent.add(p)
|
||||
vdomChild.children.forEach(child => {
|
||||
this.generate(p, child)
|
||||
this.generate(p, child, scope)
|
||||
})
|
||||
break
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function line(props) {
|
||||
export function line(props, scope) {
|
||||
const obj = new Line(
|
||||
Number(props.x1),
|
||||
Number(props.y1),
|
||||
|
@ -12,6 +12,6 @@ export function line(props) {
|
|||
parseStyle(props)
|
||||
)
|
||||
transform(props, obj)
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export function parseEvent(props, obj) {
|
||||
export function parseEvent(props, obj, scope) {
|
||||
if (!props) return
|
||||
|
||||
const tapHandler =
|
||||
|
@ -22,7 +22,11 @@ export function parseEvent(props, obj) {
|
|||
obj.on('touchend', evt => {
|
||||
if (_x !== null) {
|
||||
if (Math.abs(evt.stageX - _x) < 20 && Math.abs(evt.stageY - _y) < 20) {
|
||||
tapHandler(evt)
|
||||
if (typeof tapHandler === 'string') {
|
||||
scope[tapHandler]()
|
||||
} else {
|
||||
tapHandler(evt)
|
||||
}
|
||||
_x = null
|
||||
_y = null
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import pathTransition from '../pasition/index'
|
|||
import color from '../common/color'
|
||||
import { toSVGString } from '../common/util'
|
||||
|
||||
export function pasition(props) {
|
||||
export function pasition(props, scope) {
|
||||
const lerp = color.lerp
|
||||
const obj = new Path(props.from, parseStyle(props))
|
||||
const fs = props['from-stroke']
|
||||
|
@ -22,7 +22,7 @@ export function pasition(props) {
|
|||
obj.pasitionTo = props.to
|
||||
obj.pasitionFrom = props.from
|
||||
transform(props, obj)
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
|
||||
let stage,
|
||||
isFrom = true,
|
||||
|
|
|
@ -3,9 +3,9 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function path(props) {
|
||||
export function path(props, scope) {
|
||||
const obj = new Path(props.d, parseStyle(props))
|
||||
transform(props, obj)
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function polygon(props) {
|
||||
export function polygon(props, scope) {
|
||||
const points = props.points
|
||||
.split(/\s+|,/)
|
||||
.filter(item => item !== '')
|
||||
.map(item => Number(item))
|
||||
const obj = new Polygon(points, parseStyle(props))
|
||||
transform(props, obj)
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function polyline(props) {
|
||||
export function polyline(props, scope) {
|
||||
const points = props.points.split(/\s+|,/).map(item => Number(item))
|
||||
const obj = new Polyline(points, parseStyle(props))
|
||||
transform(props, obj)
|
||||
parseEvent(props, obj)
|
||||
parseEvent(props, obj, scope)
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { parseStyle } from './parse-style'
|
|||
import { transform } from './parse-transform'
|
||||
import { parseEvent } from './parse-event'
|
||||
|
||||
export function rect(props) {
|
||||
export function rect(props, scope) {
|
||||
const options = Object.assign(
|
||||
{
|
||||
width: 0,
|
||||
|
@ -26,7 +26,7 @@ export function rect(props) {
|
|||
|
||||
|
||||
transform(props, rect, Number(options.x), Number(options.y))
|
||||
parseEvent(props, rect)
|
||||
parseEvent(props, rect, scope)
|
||||
|
||||
return rect
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
http://tutorials.jenkov.com/svg/index.html
|
||||
|
||||
todo:
|
||||
|
||||
* 自动轮播支持 http://tavmjong.free.fr/blog/wp-content/uploads/BATMAN/batman_logos.svg (间隔时间支持)
|
||||
* debug bounds box
|
||||
* pasition easing function
|
||||
|
||||
* Transformation + tojs
|
||||
* Transformation
|
||||
* boundsX 和 boundsY
|
||||
* text
|
||||
* image
|
||||
* SVG Viewport and View Box
|
||||
* event handler
|
||||
* jsx support(extend omip)
|
||||
|
||||
vscode 安装 lit-html 使 html`内容` 高亮
|
||||
|
||||
|
||||
import testSVG from 'svg/test'
|
||||
|
||||
renderSVG(testSVG, 'canvas-id', this)
|
Loading…
Reference in New Issue