mps - support svg

This commit is contained in:
dntzhang 2019-04-16 09:53:58 +08:00
parent 4a30c5bfe0
commit 6a2041078f
88 changed files with 7714 additions and 53 deletions

View File

@ -1,12 +1,13 @@
{
"pages":[
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window":{
"backgroundTextStyle":"light",
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json"
}

136
packages/mps/cax/cax.js Executable file
View File

@ -0,0 +1,136 @@
import TWEEN from './common/tween'
import To from './common/to'
import './common/animate'
import Stage from './render/display/stage'
import WeStage from './render/display/we-stage'
import Graphics from './render/display/graphics'
import Bitmap from './render/display/bitmap'
import Text from './render/display/text'
import Group from './render/display/group'
import Sprite from './render/display/sprite'
import Shape from './render/display/shape/shape'
import RoundedRect from './render/display/shape/rounded-rect'
import ArrowPath from './render/display/shape/arrow-path'
import Ellipse from './render/display/shape/ellipse'
import Path from './render/display/shape/path'
import Button from './render/display/element/button'
import Rect from './render/display/shape/rect'
import Circle from './render/display/shape/circle'
import Polygon from './render/display/shape/polygon'
import EquilateralPolygon from './render/display/shape/equilateral-polygon'
import { setRafInterval, clearRafInterval } from './common/raf-interval'
import html from './svg/html'
import SVG from './svg/index'
To.easing = {
linear: TWEEN.Easing.Linear.None
}
const cax = {
easing: {
linear: TWEEN.Easing.Linear.None
},
util: {
randomInt: (min, max) => {
return min + Math.floor(Math.random() * (max - min + 1))
}
},
SVG,
html,
Stage,
WeStage,
Graphics,
Bitmap,
Text,
Group,
Sprite,
Shape,
ArrowPath,
Ellipse,
Path,
Button,
RoundedRect,
Rect,
Circle,
Polygon,
EquilateralPolygon,
setInterval: setRafInterval,
clearInterval: clearRafInterval,
tick: function(fn) {
return setRafInterval(fn, 16)
},
untick: function(tickId) {
clearRafInterval(tickId)
},
caxCanvasId: 0,
TWEEN,
To,
h: function(type, props, ...children) {
return { type, props, children }
}
}
;[
'Quadratic',
'Cubic',
'Quartic',
'Quintic',
'Sinusoidal',
'Exponential',
'Circular',
'Elastic',
'Back',
'Bounce'
].forEach(item => {
const itemLower = item.toLowerCase()
cax.easing[itemLower + 'In'] = TWEEN.Easing[item].In
cax.easing[itemLower + 'Out'] = TWEEN.Easing[item].Out
cax.easing[itemLower + 'InOut'] = TWEEN.Easing[item].InOut
To.easing[itemLower + 'In'] = TWEEN.Easing[item].In
To.easing[itemLower + 'Out'] = TWEEN.Easing[item].Out
To.easing[itemLower + 'InOut'] = TWEEN.Easing[item].InOut
})
const isWegame = typeof wx !== 'undefined' && wx.createCanvas
cax.loadImg = function(option) {
const img = isWegame ? wx.createImage() : new Image()
img.onload = function() {
option.complete(this)
}
img.src = option.img
}
cax.loadImgs = function(option) {
const result = []
let loaded = 0
const len = option.imgs.length
option.imgs.forEach((src, index) => {
const img = isWegame ? wx.createImage() : new Image()
img.onload = (function(i, img) {
return function() {
result[i] = img
loaded++
option.progress && option.progress(loaded / len, loaded, i, img, result)
if (loaded === len) {
option.complete && option.complete(result)
}
}
})(index, img)
img.src = src
})
}
module.exports = cax

View File

@ -0,0 +1,294 @@
import To from './to'
To.extend('rubber', [
[
'to',
[
'scaleX',
{
'0': 1.25,
'1': 300
}
],
[
'scaleY',
{
'0': 0.75,
'1': 300
}
]
],
[
'to',
[
'scaleX',
{
'0': 0.75,
'1': 100
}
],
[
'scaleY',
{
'0': 1.25,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 1.15,
'1': 100
}
],
[
'scaleY',
{
'0': 0.85,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 0.95,
'1': 150
}
],
[
'scaleY',
{
'0': 1.05,
'1': 150
}
]
],
[
'to',
[
'scaleX',
{
'0': 1.05,
'1': 100
}
],
[
'scaleY',
{
'0': 0.95,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 1,
'1': 250
}
],
[
'scaleY',
{
'0': 1,
'1': 250
}
]
]
])
To.extend('bounceIn', [
[
'to',
[
'scaleX',
{
'0': 0,
'1': 0
}
],
[
'scaleY',
{
'0': 0,
'1': 0
}
]
],
[
'to',
[
'scaleX',
{
'0': 1.35,
'1': 200
}
],
[
'scaleY',
{
'0': 1.35,
'1': 200
}
]
],
[
'to',
[
'scaleX',
{
'0': 0.9,
'1': 100
}
],
[
'scaleY',
{
'0': 0.9,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 1.1,
'1': 100
}
],
[
'scaleY',
{
'0': 1.1,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 0.95,
'1': 100
}
],
[
'scaleY',
{
'0': 0.95,
'1': 100
}
]
],
[
'to',
[
'scaleX',
{
'0': 1,
'1': 100
}
],
[
'scaleY',
{
'0': 1,
'1': 100
}
]
]
])
To.extend('flipInX', [
[
'to',
[
'rotateX',
{
'0': -90,
'1': 0
}
]
],
[
'to',
[
'rotateX',
{
'0': 20,
'1': 300
}
]
],
[
'to',
[
'rotateX',
{
'0': -20,
'1': 300
}
]
],
[
'to',
[
'rotateX',
{
'0': 10,
'1': 300
}
]
],
[
'to',
[
'rotateX',
{
'0': -5,
'1': 300
}
]
],
[
'to',
[
'rotateX',
{
'0': 0,
'1': 300
}
]
]
])
To.extend('zoomOut', [
[
'to',
[
'scaleX',
{
'0': 0,
'1': 400
}
],
[
'scaleY',
{
'0': 0,
'1': 400
}
]
]
])

View File

@ -0,0 +1,226 @@
const cache = {}
const cssColors = {
aliceblue: 0xF0F8FF,
antiquewhite: 0xFAEBD7,
aqua: 0x00FFFF,
aquamarine: 0x7FFFD4,
azure: 0xF0FFFF,
beige: 0xF5F5DC,
bisque: 0xFFE4C4,
black: 0x000000,
blanchedalmond: 0xFFEBCD,
blue: 0x0000FF,
blueviolet: 0x8A2BE2,
brown: 0xA52A2A,
burlywood: 0xDEB887,
cadetblue: 0x5F9EA0,
chartreuse: 0x7FFF00,
chocolate: 0xD2691E,
coral: 0xFF7F50,
cornflowerblue: 0x6495ED,
cornsilk: 0xFFF8DC,
crimson: 0xDC143C,
cyan: 0x00FFFF,
darkblue: 0x00008B,
darkcyan: 0x008B8B,
darkgoldenrod: 0xB8860B,
darkgray: 0xA9A9A9,
darkgrey: 0xA9A9A9,
darkgreen: 0x006400,
darkkhaki: 0xBDB76B,
darkmagenta: 0x8B008B,
darkolivegreen: 0x556B2F,
darkorange: 0xFF8C00,
darkorchid: 0x9932CC,
darkred: 0x8B0000,
darksalmon: 0xE9967A,
darkseagreen: 0x8FBC8F,
darkslateblue: 0x483D8B,
darkslategray: 0x2F4F4F,
darkslategrey: 0x2F4F4F,
darkturquoise: 0x00CED1,
darkviolet: 0x9400D3,
deeppink: 0xFF1493,
deepskyblue: 0x00BFFF,
dimgray: 0x696969,
dimgrey: 0x696969,
dodgerblue: 0x1E90FF,
firebrick: 0xB22222,
floralwhite: 0xFFFAF0,
forestgreen: 0x228B22,
fuchsia: 0xFF00FF,
gainsboro: 0xDCDCDC,
ghostwhite: 0xF8F8FF,
gold: 0xFFD700,
goldenrod: 0xDAA520,
gray: 0x808080,
grey: 0x808080,
green: 0x008000,
greenyellow: 0xADFF2F,
honeydew: 0xF0FFF0,
hotpink: 0xFF69B4,
indianred: 0xCD5C5C,
indigo: 0x4B0082,
ivory: 0xFFFFF0,
khaki: 0xF0E68C,
lavender: 0xE6E6FA,
lavenderblush: 0xFFF0F5,
lawngreen: 0x7CFC00,
lemonchiffon: 0xFFFACD,
lightblue: 0xADD8E6,
lightcoral: 0xF08080,
lightcyan: 0xE0FFFF,
lightgoldenrodyellow: 0xFAFAD2,
lightgray: 0xD3D3D3,
lightgrey: 0xD3D3D3,
lightgreen: 0x90EE90,
lightpink: 0xFFB6C1,
lightsalmon: 0xFFA07A,
lightseagreen: 0x20B2AA,
lightskyblue: 0x87CEFA,
lightslategray: 0x778899,
lightslategrey: 0x778899,
lightsteelblue: 0xB0C4DE,
lightyellow: 0xFFFFE0,
lime: 0x00FF00,
limegreen: 0x32CD32,
linen: 0xFAF0E6,
magenta: 0xFF00FF,
maroon: 0x800000,
mediumaquamarine: 0x66CDAA,
mediumblue: 0x0000CD,
mediumorchid: 0xBA55D3,
mediumpurple: 0x9370D8,
mediumseagreen: 0x3CB371,
mediumslateblue: 0x7B68EE,
mediumspringgreen: 0x00FA9A,
mediumturquoise: 0x48D1CC,
mediumvioletred: 0xC71585,
midnightblue: 0x191970,
mintcream: 0xF5FFFA,
mistyrose: 0xFFE4E1,
moccasin: 0xFFE4B5,
navajowhite: 0xFFDEAD,
navy: 0x000080,
oldlace: 0xFDF5E6,
olive: 0x808000,
olivedrab: 0x6B8E23,
orange: 0xFFA500,
orangered: 0xFF4500,
orchid: 0xDA70D6,
palegoldenrod: 0xEEE8AA,
palegreen: 0x98FB98,
paleturquoise: 0xAFEEEE,
palevioletred: 0xD87093,
papayawhip: 0xFFEFD5,
peachpuff: 0xFFDAB9,
peru: 0xCD853F,
pink: 0xFFC0CB,
plum: 0xDDA0DD,
powderblue: 0xB0E0E6,
purple: 0x800080,
red: 0xFF0000,
rosybrown: 0xBC8F8F,
royalblue: 0x4169E1,
saddlebrown: 0x8B4513,
salmon: 0xFA8072,
sandybrown: 0xF4A460,
seagreen: 0x2E8B57,
seashell: 0xFFF5EE,
sienna: 0xA0522D,
silver: 0xC0C0C0,
skyblue: 0x87CEEB,
slateblue: 0x6A5ACD,
slategray: 0x708090,
slategrey: 0x708090,
snow: 0xFFFAFA,
springgreen: 0x00FF7F,
steelblue: 0x4682B4,
tan: 0xD2B48C,
teal: 0x008080,
thistle: 0xD8BFD8,
tomato: 0xFF6347,
turquoise: 0x40E0D0,
violet: 0xEE82EE,
wheat: 0xF5DEB3,
white: 0xFFFFFF,
whitesmoke: 0xF5F5F5,
yellow: 0xFFFF00,
yellowgreen: 0x9ACD32
}
function lerp(start, end, percent){
return makeGradientColor(hexToRgb(start),hexToRgb(end),percent)
}
var hexTriplet = ("01".substr(-1) === "1" ?
// pad 6 zeros to the left
function (cssColor) {
return "#" + ("00000" + cssColor.toString(16)).substr(-6);
}
: // IE doesn't support substr with negative numbers
function (cssColor) {
var str = cssColor.toString(16);
return "#" + (new Array( str.length < 6 ? 6 - str.length + 1 : 0)).join("0") + str;
}
)
function hexToRgb(hex) {
if (cache[hex]) return cache[hex]
let cssColor = null
if (cssColors.hasOwnProperty(hex) ) {
cssColor = hex
hex = hexTriplet(cssColors[hex])
}
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
hex = hex.replace(shorthandRegex, function (m, r, g, b) {
return r + r + g + g + b + b
})
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
let key = cssColor ? cssColor : hex
cache[key] = result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null
return cache[key]
}
function makeGradientColor(color1, color2, percent) {
var newColor = {}
if (percent < 0)percent = 0
if (percent > 100)percent = 100
function makeChannel(a, b) {
return (a + Math.round((b - a) * (percent / 100)))
}
function makeColorPiece(num) {
num = Math.min(num, 255) // not more than 255
num = Math.max(num, 0) // not less than 0
var str = num.toString(16)
if (str.length < 2) {
str = "0" + str
}
return (str)
}
newColor.r = makeChannel(color1.r, color2.r)
newColor.g = makeChannel(color1.g, color2.g)
newColor.b = makeChannel(color1.b, color2.b)
newColor.cssColor = "#" +
makeColorPiece(newColor.r) +
makeColorPiece(newColor.g) +
makeColorPiece(newColor.b)
return newColor.cssColor
}
export default {
lerp,
hexToRgb,
makeGradientColor
}

View File

@ -0,0 +1,113 @@
/*!
* raf-interval v0.3.0 By dntzhang
* Github: https://github.com/dntzhang/raf-interval
* MIT Licensed.
*/
if (!Date.now) {
Date.now = function now() {
return new Date().getTime()
}
}
let queue = [],
id = -1,
ticking = false,
tickId = null,
now = Date.now,
lastTime = 0,
vendors = ['ms', 'moz', 'webkit', 'o'],
x = 0,
isWeapp = typeof wx !== 'undefined' && !wx.createCanvas,
isWegame = typeof wx !== 'undefined' && wx.createCanvas,
isBrowser = typeof window !== 'undefined'
let raf = isBrowser ? window.requestAnimationFrame : null
let caf = isBrowser ? window.cancelAnimationFrame : null
function mockRaf(callback, element) {
let currTime = now()
let timeToCall = Math.max(0, 16 - (currTime - lastTime))
let id = setTimeout(function() {
callback(currTime + timeToCall)
}, timeToCall)
lastTime = currTime + timeToCall
return id
}
function mockCaf(id) {
clearTimeout(id)
}
if (isBrowser) {
window.setRafInterval = setRafInterval
window.clearRafInterval = clearRafInterval
for (; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']
window.cancelAnimationFrame =
window[vendors[x] + 'CancelAnimationFrame'] ||
window[vendors[x] + 'CancelRequestAnimationFrame']
}
if (!raf) {
raf = mockRaf
caf = mockCaf
window.requestAnimationFrame = raf
window.cancelAnimationFrame = caf
}
} else if (isWeapp) {
raf = mockRaf
caf = mockCaf
} else if (isWegame) {
raf = requestAnimationFrame
caf = cancelAnimationFrame
}
export function setRafInterval(fn, interval) {
id++
queue.push({ id: id, fn: fn, interval: interval, lastTime: now() })
if (!ticking) {
let tick = function() {
tickId = raf(tick)
each(queue, function(item) {
if (item.interval < 17 || now() - item.lastTime >= item.interval) {
item.fn()
item.lastTime = now()
}
})
}
ticking = true
tick()
}
return id
}
export function clearRafInterval(id) {
let i = 0,
len = queue.length
for (; i < len; i++) {
if (id === queue[i].id) {
queue.splice(i, 1)
break
}
}
if (queue.length === 0) {
caf(tickId)
ticking = false
}
}
function each(arr, fn) {
if (Array.prototype.forEach) {
arr.forEach(fn)
} else {
let i = 0,
len = arr.length
for (; i < len; i++) {
fn(arr[i], i)
}
}
}

264
packages/mps/cax/common/to.js Executable file
View File

@ -0,0 +1,264 @@
import TWEEN from './tween'
import { setRafInterval, clearRafInterval } from './raf-interval'
class To {
constructor(element) {
this.element = element
this.cmds = []
this.index = 0
this.tweens = []
this._pause = false
this.loop = setRafInterval(function() {
TWEEN.update()
}, 15)
this.cycleCount = 0
}
to(target, duration, easing) {
this.cmds.push(['to'])
if (arguments.length !== 0) {
for (let key in target) {
this.set(key, target[key], duration || 0, easing)
}
}
return this
}
set(prop, value, duration, easing) {
this.cmds[this.cmds.length - 1].push([prop, [value, duration, easing]])
return this
}
x() {
this.cmds[this.cmds.length - 1].push(['x', arguments])
return this
}
y() {
this.cmds[this.cmds.length - 1].push(['y', arguments])
return this
}
z() {
this.cmds[this.cmds.length - 1].push(['z', arguments])
return this
}
rotation() {
this.cmds[this.cmds.length - 1].push(['rotation', arguments])
return this
}
scaleX() {
this.cmds[this.cmds.length - 1].push(['scaleX', arguments])
return this
}
scaleY() {
this.cmds[this.cmds.length - 1].push(['scaleY', arguments])
return this
}
skewX() {
this.cmds[this.cmds.length - 1].push(['skewX', arguments])
return this
}
skewY() {
this.cmds[this.cmds.length - 1].push(['skewY', arguments])
return this
}
originX() {
this.cmds[this.cmds.length - 1].push(['originX', arguments])
return this
}
originY() {
this.cmds[this.cmds.length - 1].push(['originY', arguments])
return this
}
alpha() {
this.cmds[this.cmds.length - 1].push(['alpha', arguments])
return this
}
begin(fn) {
this.cmds[this.cmds.length - 1].begin = fn
return this
}
progress(fn) {
this.cmds[this.cmds.length - 1].progress = fn
return this
}
end(fn) {
this.cmds[this.cmds.length - 1].end = fn
return this
}
wait() {
this.cmds.push(['wait', arguments])
return this
}
then() {
this.cmds.push(['then', arguments])
return this
}
cycle() {
this.cmds.push(['cycle', arguments])
return this
}
start() {
if (this._pause) return
var len = this.cmds.length
if (this.index < len) {
this.exec(this.cmds[this.index], this.index === len - 1)
} else {
clearRafInterval(this.loop)
}
return this
}
pause() {
this._pause = true
for (var i = 0, len = this.tweens.length; i < len; i++) {
this.tweens[i].pause()
}
if (this.currentTask === 'wait') {
this.timeout -= new Date() - this.currentTaskBegin
this.currentTaskBegin = new Date()
}
}
toggle() {
if (this._pause) {
this.play()
} else {
this.pause()
}
}
play() {
this._pause = false
for (var i = 0, len = this.tweens.length; i < len; i++) {
this.tweens[i].play()
}
var self = this
if (this.currentTask === 'wait') {
setTimeout(function() {
if (self._pause) return
self.index++
self.start()
if (self.index === self.cmds.length && self.complete) self.complete()
}, this.timeout)
}
}
stop() {
for (var i = 0, len = this.tweens.length; i < len; i++) {
this.tweens[i].stop()
}
this.cmds.length = 0
}
animate(name) {
this.cmds = this.cmds.concat(To.animationMap[name] || [])
return this
}
exec(cmd, last) {
var len = cmd.length,
self = this
this.currentTask = cmd[0]
switch (this.currentTask) {
case 'to':
self.stepCompleteCount = 0
for (var i = 1; i < len; i++) {
var task = cmd[i]
var ease = task[1][2]
var target = {}
var prop = task[0]
target[prop] = task[1][0]
var t = new TWEEN.Tween(this.element)
.to(target, task[1][1])
.onStart(function() {
if (cmd.begin) cmd.begin.call(self.element, self.element)
})
.onUpdate(function() {
if (cmd.progress) cmd.progress.call(self.element, self.element)
// self.element[prop] = this[prop];
})
.easing(ease || TWEEN.Easing.Linear.None)
.onComplete(function() {
self.stepCompleteCount++
if (self.stepCompleteCount === len - 1) {
if (cmd.end) cmd.end.call(self.element, self.element)
if (last && self.complete) self.complete()
self.index++
self.start()
}
})
.start()
this.tweens.push(t)
}
break
case 'wait':
this.currentTaskBegin = new Date()
this.timeout = cmd[1][0]
setTimeout(function() {
if (self._pause) return
self.index++
self.start()
if (cmd.end) cmd.end.call(self.element, self.element)
if (last && self.complete) self.complete()
}, cmd[1][0])
break
case 'then':
var arg = cmd[1][0]
arg.index = 0
arg.complete = function() {
self.index++
self.start()
if (last && self.complete) self.complete()
}
arg.start()
break
case 'cycle':
var count = cmd[1][1]
if (count === undefined) {
self.index = cmd[1][0] || 0
self.start()
} else {
if (count && self.cycleCount === count) {
self.index++
self.start()
if (last && self.complete) self.complete()
} else {
self.cycleCount++
self.index = cmd[1][0]
self.start()
}
}
break
}
}
}
To.get = function(element) {
var to = new To(element)
return to
}
To.animationMap = {}
To.extend = function(animationName, cmds) {
To.animationMap[animationName] = cmds
}
export default To

784
packages/mps/cax/common/tween.js Executable file
View File

@ -0,0 +1,784 @@
/**
* Tween.js - Licensed under the MIT license
* https://github.com/tweenjs/tween.js
* ----------------------------------------------
*
* See https://github.com/tweenjs/tween.js/graphs/contributors for the full list of contributors.
* Thank you all, you're awesome!
*/
var _Group = function() {
this._tweens = {}
this._tweensAddedDuringUpdate = {}
}
_Group.prototype = {
getAll: function() {
return Object.keys(this._tweens).map(
function(tweenId) {
return this._tweens[tweenId]
}.bind(this)
)
},
removeAll: function() {
this._tweens = {}
},
add: function(tween) {
this._tweens[tween.getId()] = tween
this._tweensAddedDuringUpdate[tween.getId()] = tween
},
remove: function(tween) {
delete this._tweens[tween.getId()]
delete this._tweensAddedDuringUpdate[tween.getId()]
},
update: function(time, preserve) {
var tweenIds = Object.keys(this._tweens)
if (tweenIds.length === 0) {
return false
}
time = time !== undefined ? time : TWEEN.now()
// Tweens are updated in "batches". If you add a new tween during an update, then the
// new tween will be updated in the next batch.
// If you remove a tween during an update, it may or may not be updated. However,
// if the removed tween was added during the current batch, then it will not be updated.
while (tweenIds.length > 0) {
this._tweensAddedDuringUpdate = {}
for (var i = 0; i < tweenIds.length; i++) {
var tween = this._tweens[tweenIds[i]]
if (tween && tween.update(time) === false) {
tween._isPlaying = false
if (!preserve) {
delete this._tweens[tweenIds[i]]
}
}
}
tweenIds = Object.keys(this._tweensAddedDuringUpdate)
}
return true
}
}
var TWEEN = new _Group()
TWEEN.Group = _Group
TWEEN._nextId = 0
TWEEN.nextId = function() {
return TWEEN._nextId++
}
// Include a performance.now polyfill.
// In node.js, use process.hrtime.
if (typeof window === 'undefined' && typeof process !== 'undefined') {
if (typeof wx !== 'undefined') {
TWEEN.now = Date.now
} else {
TWEEN.now = function() {
var time = process.hrtime()
// Convert [seconds, nanoseconds] to milliseconds.
return time[0] * 1000 + time[1] / 1000000
}
}
} else if (
typeof window !== 'undefined' &&
// In a browser, use window.performance.now if it is available.
window.performance !== undefined &&
window.performance.now !== undefined
) {
// This must be bound, because directly assigning this function
// leads to an invocation exception in Chrome.
TWEEN.now = window.performance.now.bind(window.performance)
} else if (Date.now !== undefined) {
// Use Date.now if it is available.
TWEEN.now = Date.now
} else {
// Otherwise, use 'new Date().getTime()'.
TWEEN.now = function() {
return new Date().getTime()
}
}
TWEEN.Tween = function(object, group) {
this._object = object
this._valuesStart = {}
this._valuesEnd = {}
this._valuesStartRepeat = {}
this._duration = 1000
this._repeat = 0
this._repeatDelayTime = undefined
this._yoyo = false
this._isPlaying = false
this._reversed = false
this._delayTime = 0
this._startTime = null
this._easingFunction = TWEEN.Easing.Linear.None
this._interpolationFunction = TWEEN.Interpolation.Linear
this._chainedTweens = []
this._onStartCallback = null
this._onStartCallbackFired = false
this._onUpdateCallback = null
this._onCompleteCallback = null
this._onStopCallback = null
this._group = group || TWEEN
this._id = TWEEN.nextId()
this._paused = false
this._passTime = null
}
TWEEN.Tween.prototype = {
getId: function getId() {
return this._id
},
toggle() {
if (this._paused) {
this.play()
} else {
this.pause()
}
},
pause: function() {
this._paused = true
var pauseTime = TWEEN.now()
this._passTime = pauseTime - this._startTime
},
play: function() {
this._paused = false
var nowTime = TWEEN.now()
this._startTime = nowTime - this._passTime
},
isPlaying: function isPlaying() {
return this._isPlaying
},
to: function to(properties, duration) {
this._valuesEnd = properties
if (duration !== undefined) {
this._duration = duration
}
return this
},
start: function start(time) {
this._group.add(this)
this._isPlaying = true
this._onStartCallbackFired = false
this._startTime =
time !== undefined
? typeof time === 'string'
? TWEEN.now() + parseFloat(time)
: time
: TWEEN.now()
this._startTime += this._delayTime
for (var property in this._valuesEnd) {
// Check if an Array was provided as property value
if (this._valuesEnd[property] instanceof Array) {
if (this._valuesEnd[property].length === 0) {
continue
}
// Create a local copy of the Array with the start value at the front
this._valuesEnd[property] = [this._object[property]].concat(
this._valuesEnd[property]
)
}
// If `to()` specifies a property that doesn't exist in the source object,
// we should not set that property in the object
if (this._object[property] === undefined) {
continue
}
// Save the starting value.
this._valuesStart[property] = this._object[property]
if (this._valuesStart[property] instanceof Array === false) {
this._valuesStart[property] *= 1.0 // Ensures we're using numbers, not strings
}
this._valuesStartRepeat[property] = this._valuesStart[property] || 0
}
return this
},
stop: function stop() {
if (!this._isPlaying) {
return this
}
this._group.remove(this)
this._isPlaying = false
if (this._onStopCallback !== null) {
this._onStopCallback(this._object)
}
this.stopChainedTweens()
return this
},
end: function end() {
this.update(this._startTime + this._duration)
return this
},
stopChainedTweens: function stopChainedTweens() {
for (
var i = 0, numChainedTweens = this._chainedTweens.length;
i < numChainedTweens;
i++
) {
this._chainedTweens[i].stop()
}
},
group: function group(group) {
this._group = group
return this
},
delay: function delay(amount) {
this._delayTime = amount
return this
},
repeat: function repeat(times) {
this._repeat = times
return this
},
repeatDelay: function repeatDelay(amount) {
this._repeatDelayTime = amount
return this
},
yoyo: function yoyo(yy) {
this._yoyo = yy
return this
},
easing: function easing(eas) {
this._easingFunction = eas
return this
},
interpolation: function interpolation(inter) {
this._interpolationFunction = inter
return this
},
chain: function chain() {
this._chainedTweens = arguments
return this
},
onStart: function onStart(callback) {
this._onStartCallback = callback
return this
},
onUpdate: function onUpdate(callback) {
this._onUpdateCallback = callback
return this
},
onComplete: function onComplete(callback) {
this._onCompleteCallback = callback
return this
},
onStop: function onStop(callback) {
this._onStopCallback = callback
return this
},
update: function update(time) {
if (this._paused) return true
var property
var elapsed
var value
if (time < this._startTime) {
return true
}
if (this._onStartCallbackFired === false) {
if (this._onStartCallback !== null) {
this._onStartCallback(this._object)
}
this._onStartCallbackFired = true
}
elapsed = (time - this._startTime) / this._duration
elapsed = this._duration === 0 || elapsed > 1 ? 1 : elapsed
value = this._easingFunction(elapsed)
for (property in this._valuesEnd) {
// Don't update properties that do not exist in the source object
if (this._valuesStart[property] === undefined) {
continue
}
var start = this._valuesStart[property] || 0
var end = this._valuesEnd[property]
if (end instanceof Array) {
this._object[property] = this._interpolationFunction(end, value)
} else {
// Parses relative end values with start as base (e.g.: +10, -3)
if (typeof end === 'string') {
if (end.charAt(0) === '+' || end.charAt(0) === '-') {
end = start + parseFloat(end)
} else {
end = parseFloat(end)
}
}
// Protect against non numeric properties.
if (typeof end === 'number') {
this._object[property] = start + (end - start) * value
}
}
}
if (this._onUpdateCallback !== null) {
this._onUpdateCallback(this._object)
}
if (elapsed === 1) {
if (this._repeat > 0) {
if (isFinite(this._repeat)) {
this._repeat--
}
// Reassign starting values, restart by making startTime = now
for (property in this._valuesStartRepeat) {
if (typeof this._valuesEnd[property] === 'string') {
this._valuesStartRepeat[property] =
this._valuesStartRepeat[property] +
parseFloat(this._valuesEnd[property])
}
if (this._yoyo) {
var tmp = this._valuesStartRepeat[property]
this._valuesStartRepeat[property] = this._valuesEnd[property]
this._valuesEnd[property] = tmp
}
this._valuesStart[property] = this._valuesStartRepeat[property]
}
if (this._yoyo) {
this._reversed = !this._reversed
}
if (this._repeatDelayTime !== undefined) {
this._startTime = time + this._repeatDelayTime
} else {
this._startTime = time + this._delayTime
}
return true
} else {
if (this._onCompleteCallback !== null) {
this._onCompleteCallback(this._object)
}
for (
var i = 0, numChainedTweens = this._chainedTweens.length;
i < numChainedTweens;
i++
) {
// Make the chained tweens start exactly at the time they should,
// even if the `update()` method was called way past the duration of the tween
this._chainedTweens[i].start(this._startTime + this._duration)
}
return false
}
}
return true
}
}
TWEEN.Easing = {
Linear: {
None: function(k) {
return k
}
},
Quadratic: {
In: function(k) {
return k * k
},
Out: function(k) {
return k * (2 - k)
},
InOut: function(k) {
if ((k *= 2) < 1) {
return 0.5 * k * k
}
return -0.5 * (--k * (k - 2) - 1)
}
},
Cubic: {
In: function(k) {
return k * k * k
},
Out: function(k) {
return --k * k * k + 1
},
InOut: function(k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k
}
return 0.5 * ((k -= 2) * k * k + 2)
}
},
Quartic: {
In: function(k) {
return k * k * k * k
},
Out: function(k) {
return 1 - --k * k * k * k
},
InOut: function(k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k * k
}
return -0.5 * ((k -= 2) * k * k * k - 2)
}
},
Quintic: {
In: function(k) {
return k * k * k * k * k
},
Out: function(k) {
return --k * k * k * k * k + 1
},
InOut: function(k) {
if ((k *= 2) < 1) {
return 0.5 * k * k * k * k * k
}
return 0.5 * ((k -= 2) * k * k * k * k + 2)
}
},
Sinusoidal: {
In: function(k) {
return 1 - Math.cos((k * Math.PI) / 2)
},
Out: function(k) {
return Math.sin((k * Math.PI) / 2)
},
InOut: function(k) {
return 0.5 * (1 - Math.cos(Math.PI * k))
}
},
Exponential: {
In: function(k) {
return k === 0 ? 0 : Math.pow(1024, k - 1)
},
Out: function(k) {
return k === 1 ? 1 : 1 - Math.pow(2, -10 * k)
},
InOut: function(k) {
if (k === 0) {
return 0
}
if (k === 1) {
return 1
}
if ((k *= 2) < 1) {
return 0.5 * Math.pow(1024, k - 1)
}
return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2)
}
},
Circular: {
In: function(k) {
return 1 - Math.sqrt(1 - k * k)
},
Out: function(k) {
return Math.sqrt(1 - --k * k)
},
InOut: function(k) {
if ((k *= 2) < 1) {
return -0.5 * (Math.sqrt(1 - k * k) - 1)
}
return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1)
}
},
Elastic: {
In: function(k) {
if (k === 0) {
return 0
}
if (k === 1) {
return 1
}
return -Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI)
},
Out: function(k) {
if (k === 0) {
return 0
}
if (k === 1) {
return 1
}
return Math.pow(2, -10 * k) * Math.sin((k - 0.1) * 5 * Math.PI) + 1
},
InOut: function(k) {
if (k === 0) {
return 0
}
if (k === 1) {
return 1
}
k *= 2
if (k < 1) {
return (
-0.5 * Math.pow(2, 10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI)
)
}
return (
0.5 * Math.pow(2, -10 * (k - 1)) * Math.sin((k - 1.1) * 5 * Math.PI) + 1
)
}
},
Back: {
In: function(k) {
var s = 1.70158
return k * k * ((s + 1) * k - s)
},
Out: function(k) {
var s = 1.70158
return --k * k * ((s + 1) * k + s) + 1
},
InOut: function(k) {
var s = 1.70158 * 1.525
if ((k *= 2) < 1) {
return 0.5 * (k * k * ((s + 1) * k - s))
}
return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2)
}
},
Bounce: {
In: function(k) {
return 1 - TWEEN.Easing.Bounce.Out(1 - k)
},
Out: function(k) {
if (k < 1 / 2.75) {
return 7.5625 * k * k
} else if (k < 2 / 2.75) {
return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75
} else if (k < 2.5 / 2.75) {
return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375
} else {
return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375
}
},
InOut: function(k) {
if (k < 0.5) {
return TWEEN.Easing.Bounce.In(k * 2) * 0.5
}
return TWEEN.Easing.Bounce.Out(k * 2 - 1) * 0.5 + 0.5
}
}
}
TWEEN.Interpolation = {
Linear: function(v, k) {
var m = v.length - 1
var f = m * k
var i = Math.floor(f)
var fn = TWEEN.Interpolation.Utils.Linear
if (k < 0) {
return fn(v[0], v[1], f)
}
if (k > 1) {
return fn(v[m], v[m - 1], m - f)
}
return fn(v[i], v[i + 1 > m ? m : i + 1], f - i)
},
Bezier: function(v, k) {
var b = 0
var n = v.length - 1
var pw = Math.pow
var bn = TWEEN.Interpolation.Utils.Bernstein
for (var i = 0; i <= n; i++) {
b += pw(1 - k, n - i) * pw(k, i) * v[i] * bn(n, i)
}
return b
},
CatmullRom: function(v, k) {
var m = v.length - 1
var f = m * k
var i = Math.floor(f)
var fn = TWEEN.Interpolation.Utils.CatmullRom
if (v[0] === v[m]) {
if (k < 0) {
i = Math.floor((f = m * (1 + k)))
}
return fn(v[(i - 1 + m) % m], v[i], v[(i + 1) % m], v[(i + 2) % m], f - i)
} else {
if (k < 0) {
return v[0] - (fn(v[0], v[0], v[1], v[1], -f) - v[0])
}
if (k > 1) {
return v[m] - (fn(v[m], v[m], v[m - 1], v[m - 1], f - m) - v[m])
}
return fn(
v[i ? i - 1 : 0],
v[i],
v[m < i + 1 ? m : i + 1],
v[m < i + 2 ? m : i + 2],
f - i
)
}
},
Utils: {
Linear: function(p0, p1, t) {
return (p1 - p0) * t + p0
},
Bernstein: function(n, i) {
var fc = TWEEN.Interpolation.Utils.Factorial
return fc(n) / fc(i) / fc(n - i)
},
Factorial: (function() {
var a = [1]
return function(n) {
var s = 1
if (a[n]) {
return a[n]
}
for (var i = n; i > 1; i--) {
s *= i
}
a[n] = s
return s
}
})(),
CatmullRom: function(p0, p1, p2, p3, t) {
var v0 = (p2 - p0) * 0.5
var v1 = (p3 - p1) * 0.5
var t2 = t * t
var t3 = t * t2
return (
(2 * p1 - 2 * p2 + v0 + v1) * t3 +
(-3 * p1 + 3 * p2 - 2 * v0 - v1) * t2 +
v0 * t +
p1
)
}
}
}
// UMD (Universal Module Definition)
;(function(root) {
if (typeof module !== 'undefined' && typeof exports === 'object') {
// Node.js
module.exports = TWEEN
} else if (root !== undefined) {
// Global variable
root.TWEEN = TWEEN
}
})(this)

92
packages/mps/cax/common/util.js Executable file
View File

@ -0,0 +1,92 @@
export function getImageInWx(img, callback) {
if (
(img.indexOf('https://') === -1 && img.indexOf('http://') === -1) ||
img.indexOf('http://tmp/') === 0
) {
wx.getImageInfo({
src: img,
success: info => {
callback({
img: img,
width: info.width,
height: info.height
})
}
})
} else {
wx.downloadFile({
url: img,
success: res => {
if (res.statusCode === 200) {
wx.getImageInfo({
src: res.tempFilePath,
success: info => {
callback({
img: res.tempFilePath,
width: info.width,
height: info.height
})
}
})
}
}
})
}
}
function getGlobal() {
if (
typeof global !== 'object' ||
!global ||
global.Math !== Math ||
global.Array !== Array
) {
if (typeof self !== 'undefined') {
return self
} else if (typeof window !== 'undefined') {
return window
} else if (typeof global !== 'undefined') {
return global
}
return (function() {
return this
})()
}
return global
}
export function toSVGString(shapes) {
return shapes
.map(function(shape) {
shape.forEach(function(point, idx) {
if (!idx) {
/*
* 若是第一个点数组那么对该点数组的处理是前面加M,然后前两个点后面加C
* */
point.splice(2, 0, 'C')
point.unshift('M')
} else {
/*
* 除了第一个点数据外,所有的点数组的前两个点删除掉
* */
point.splice(0, 2, 'C')
}
})
return shape
.map(function(point) {
return point.join(' ')
})
.join('')
})
.join('')
}
const root = getGlobal()
export default {
getImageInWx,
root,
isWeapp:
typeof wx !== 'undefined' && !wx.createCanvas && wx.createCanvasContext,
isWegame: typeof wx !== 'undefined' && wx.createCanvas
}

46
packages/mps/cax/index.js Executable file
View File

@ -0,0 +1,46 @@
import cax from './index'
Component({
/**
* 组件的属性列表
*/
properties: {
option: {
type: Object
}
},
/**
* 组件的初始数据
*/
data: {
width: 0,
height: 0,
id: 'caxCanvas' + cax.caxCanvasId++,
index: cax.caxCanvasId - 1
},
/**
* 组件的方法列表
*/
methods: {
getCaxCanvasId: function() {
return this.data.id
},
touchStart: function(evt) {
this.stage.touchStartHandler(evt)
this.stage.touchStart && this.stage.touchStart(evt)
},
touchMove: function(evt) {
this.stage.touchMoveHandler(evt)
this.stage.touchMove && this.stage.touchMove(evt)
},
touchEnd: function(evt) {
this.stage.touchEndHandler(evt)
this.stage.touchEnd && this.stage.touchEnd(evt)
}
}
})

4
packages/mps/cax/index.json Executable file
View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

12
packages/mps/cax/index.wxml Executable file
View File

@ -0,0 +1,12 @@
<canvas
class="cax-canvas"
bindtouchstart="touchStart"
bindtouchmove="touchMove"
bindtouchend="touchEnd"
canvas-id="{{ id }}"
style="width: {{width}}px; height: {{height}}px;">
<slot></slot>
</canvas>
<canvas class="cax-canvas-hit" canvas-id="{{id}}Hit" style='width:1px;height:1px;display: none;'></canvas>
<canvas class="cax-canvas-measure" canvas-id="measure{{index}}" style='width:1px;height:1px;display: none;'></canvas>

3
packages/mps/cax/index.wxss Executable file
View File

@ -0,0 +1,3 @@
.cax-canvas{
border: 1px solid;
}

View File

@ -0,0 +1,469 @@
import a2c from '../render/base/a2c'
import parser from './svg-path-parser.js'
import { sort, sortCurves } from './sort.js'
let pasition = {}
pasition.parser = parser
pasition.lerpCurve = function (pathA, pathB, t) {
return pasition.lerpPoints(pathA[0], pathA[1], pathB[0], pathB[1], t)
.concat(pasition.lerpPoints(pathA[2], pathA[3], pathB[2], pathB[3], t))
.concat(pasition.lerpPoints(pathA[4], pathA[5], pathB[4], pathB[5], t))
.concat(pasition.lerpPoints(pathA[6], pathA[7], pathB[6], pathB[7], t))
}
pasition.lerpPoints = function (x1, y1, x2, y2, t) {
return [x1 + (x2 - x1) * t, y1 + (y2 - y1) * t]
}
pasition.q2b = function (x1, y1, x2, y2, x3, y3) {
return [x1, y1, (x1 + 2 * x2) / 3, (y1 + 2 * y2) / 3, (x3 + 2 * x2 )/ 3, (y3 + 2 * y2) / 3, x3, y3]
}
pasition.path2shapes = function (path) {
//https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Paths
//M = moveto
//L = lineto
//H = horizontal lineto
//V = vertical lineto
//C = curveto
//S = smooth curveto
//Q = quadratic Belzier curve
//T = smooth quadratic Belzier curveto
//A = elliptical Arc
//Z = closepath
//以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位(从上一个点开始)。
let cmds = pasition.parser(path),
preX = 0,
preY = 0,
j = 0,
len = cmds.length,
shapes = [],
current = null,
closeX,
closeY,
preCX,
preCY,
sLen,
curves,
lastCurve,
tempX,
tempY
for (; j < len; j++) {
let item = cmds[j]
let action = item[0]
let preItem = cmds[j - 1]
switch (action) {
case 'm':
sLen = shapes.length
shapes[sLen] = []
current = shapes[sLen]
preX = preX+item[1]
preY = preY+item[2]
break
case 'M':
sLen = shapes.length
shapes[sLen] = []
current = shapes[sLen]
preX = item[1]
preY = item[2]
break
case 'l':
current.push([preX, preY, preX, preY, preX, preY, preX+item[1],preY+item[2]])
preX += item[1]
preY += item[2]
break
case 'L':
current.push([preX, preY, item[1], item[2], item[1], item[2], item[1], item[2]])
preX = item[1]
preY = item[2]
break
case 'h':
current.push([preX, preY, preX, preY, preX, preY, preX + item[1], preY])
preX += item[1]
break
case 'H':
current.push([preX, preY, item[1], preY, item[1], preY, item[1], preY])
preX = item[1]
break
case 'v':
current.push([preX, preY, preX, preY, preX, preY, preX, preY + item[1]])
preY += item[1]
break
case 'V':
current.push([preX, preY, preX, item[1], preX, item[1], preX, item[1]])
preY = item[1]
break
case 'C':
current.push([preX, preY, item[1], item[2], item[3], item[4], item[5], item[6]])
preX = item[5]
preY = item[6]
break
case 'S':
if(preItem[0] ==='C'||preItem[0] ==='c'){
current.push([preX, preY, preX + preItem[5] - preItem[3], preY + preItem[6] - preItem[4], item[1], item[2], item[3], item[4]])
}else if(preItem[0] ==='S'||preItem[0] ==='s'){
current.push([preX, preY, preX+ preItem[3] - preItem[1] ,preY+ preItem[4] - preItem[2], item[1], item[2], item[3], item[4]])
} else {
current.push([preX, preY, preX, preY, item[1], item[2], item[3], item[4]])
}
preX = item[3]
preY = item[4]
break
case 'c':
current.push([preX, preY, preX + item[1], preY + item[2], preX + item[3], preY + item[4], preX + item[5], preY + item[6]])
preX = preX + item[5]
preY = preY + item[6]
break
case 's':
if(preItem[0] ==='C'||preItem[0] ==='c'){
current.push([preX, preY, preX+ preItem[5] - preItem[3] ,preY+ preItem[6] - preItem[4],preX+ item[1],preY+ item[2], preX+item[3],preY+ item[4]])
}else if(preItem[0] ==='S'||preItem[0] ==='s' ){
current.push([preX, preY, preX+ preItem[3] - preItem[1] ,preY+ preItem[4] - preItem[2],preX+ item[1],preY+ item[2], preX+item[3],preY+ item[4]])
}
preX = preX+item[3]
preY = preY+item[4]
break
case 'a':
curves = a2c(preX, preY, item[1], item[2], item[3], item[4], item[5], preX + item[6], preY + item[7])
curves.push([preX,preY,curves[0], curves[1], curves[2], curves[3], curves[4], curves[5]])
tempX = curves[4]
tempY = curves[5]
for (let i = 6, len = curves.length; i < len; i += 6) {
curves.push([tempX, tempY, curves[i], curves[i + 1], curves[i + 2], curves[i + 3], curves[i + 4], curves[i + 5]])
tempX = curves[i + 4]
tempY = curves[i + 5]
}
preX = preX + item[6]
preY = preY + item[7]
break
case 'A':
curves = a2c(preX, preY, item[1], item[2], item[3], item[4], item[5], item[6], item[7])
curves.push([preX,preY,curves[0], curves[1], curves[2], curves[3], curves[4], curves[5]])
tempX = curves[4]
tempY = curves[5]
for (let i = 6, len = curves.length; i < len; i += 6) {
curves.push([tempX, tempY, curves[i], curves[i + 1], curves[i + 2], curves[i + 3], curves[i + 4], curves[i + 5]])
tempX = curves[i + 4]
tempY = curves[i + 5]
}
preX = lastCurve.x
preY = lastCurve.y
break
case 'Q':
current.push(pasition.q2b(preX,preY, item[1], item[2], item[3], item[4]))
preX = item[3]
preY = item[4]
break
case 'q':
current.push(pasition.q2b(preX,preY,preX+item[1], preY+item[2],item[3]+preX,item[4]+preY))
preX += item[3]
preY += item[4]
break
case 'T':
if(preItem[0] ==='Q'|| preItem[0] ==='q') {
preCX = preX + preItem[3] - preItem[1]
preCY = preY + preItem[4] - preItem[2]
current.push(pasition.q2b(preX, preY, preCX, preCY, item[1], item[2]))
}else if(preItem[0] ==='T'|| preItem[0] ==='t' ) {
current.push(pasition.q2b(preX, preY, preX + preX - preCX, preY + preY - preCY, item[1], item[2]))
preCX = preX + preX - preCX
preCY = preY + preY - preCY
}
preX = item[1]
preY = item[2]
break
case 't':
if(preItem[0] ==='Q'|| preItem[0] ==='q') {
preCX = preX + preItem[3] - preItem[1]
preCY = preY + preItem[4] - preItem[2]
current.push(pasition.q2b(preX, preY, preCX, preCY,preX+ item[1],preY+ item[2]))
}else if(preItem[0] ==='T'|| preItem[0] ==='t' ) {
current.push(pasition.q2b(preX, preY, preX + preX - preCX, preY + preY - preCY, preX+item[1], preY+item[2]))
preCX = preX + preX - preCX
preCY = preY + preY - preCY
}
preX += item[1]
preY += item[2]
break
case 'Z':
closeX = current[0][0]
closeY = current[0][1]
current.push([preX, preY, closeX, closeY, closeX, closeY, closeX, closeY])
break
case 'z':
closeX = current[0][0]
closeY = current[0][1]
current.push([preX, preY, closeX, closeY, closeX, closeY, closeX, closeY])
break
}
}
return shapes
}
pasition._upCurves = function (curves, count) {
let i = 0,
index = 0,
len = curves.length
for (; i < count; i++) {
curves.push(curves[index].slice(0))
index++
if(index > len-1) {
index -= len
}
}
}
function split(x1, y1, x2, y2, x3, y3, x4, y4, t) {
return {
left: _split(x1, y1, x2, y2, x3, y3, x4, y4, t),
right: _split(x4, y4, x3, y3, x2, y2, x1, y1, 1 - t, true)
}
}
function _split(x1, y1, x2, y2, x3, y3, x4, y4, t, reverse) {
let x12 = (x2 - x1) * t + x1
let y12 = (y2 - y1) * t + y1
let x23 = (x3 - x2) * t + x2
let y23 = (y3 - y2) * t + y2
let x34 = (x4 - x3) * t + x3
let y34 = (y4 - y3) * t + y3
let x123 = (x23 - x12) * t + x12
let y123 = (y23 - y12) * t + y12
let x234 = (x34 - x23) * t + x23
let y234 = (y34 - y23) * t + y23
let x1234 = (x234 - x123) * t + x123
let y1234 = (y234 - y123) * t + y123
if(reverse) {
return [x1234, y1234, x123, y123, x12, y12, x1, y1]
}
return [x1, y1, x12, y12, x123, y123, x1234, y1234]
}
pasition._splitCurves = function (curves, count) {
let i = 0,
index = 0
for (; i < count; i++) {
let curve = curves[index]
let cs = split(curve[0],curve[1],curve[2],curve[3],curve[4],curve[5],curve[6],curve[7],0.5)
curves.splice(index,1)
curves.splice(index,0,cs.left,cs.right)
index+=2
if(index >= curves.length-1) {
index = 0
}
}
}
pasition._upShapes = function (shapes, count) {
for (let i = 0; i < count; i++) {
let shape = shapes[shapes.length - 1]
let newShape = []
shape.forEach(function (curve) {
newShape.push(curve.slice(0))
})
shapes.push(newShape)
}
}
pasition._subShapes= function (shapes, count) {
for (let i = 0; i < count; i++) {
let shape = shapes[shapes.length - 1]
let newShape = []
let x = shape[0][0],
y = shape[0][1]
shape.forEach(function () {
newShape.push([x, y, x, y, x, y, x, y])
})
shapes.push(newShape)
}
}
pasition.lerp = function (pathA, pathB, t) {
return pasition._lerp( pasition.path2shapes(pathA), pasition.path2shapes(pathB), t)
}
pasition.MIM_CURVES_COUNT = 100
pasition._preprocessing = function(pathA, pathB) {
let lenA = pathA.length,
lenB = pathB.length,
clonePathA = JSON.parse(JSON.stringify(pathA)),
clonePathB = JSON.parse(JSON.stringify(pathB))
if (lenA > lenB) {
pasition._subShapes(clonePathB, lenA - lenB)
} else if (lenA < lenB) {
pasition._upShapes(clonePathA, lenB - lenA)
}
clonePathA = sort(clonePathA, clonePathB)
clonePathA.forEach(function (curves, index) {
let lenA = curves.length,
lenB = clonePathB[index].length
if (lenA > lenB) {
if (lenA < pasition.MIM_CURVES_COUNT) {
pasition._splitCurves(curves, pasition.MIM_CURVES_COUNT - lenA)
pasition._splitCurves(clonePathB[index], pasition.MIM_CURVES_COUNT - lenB)
} else {
pasition._splitCurves(clonePathB[index], lenA - lenB)
}
} else if (lenA < lenB) {
if (lenB < pasition.MIM_CURVES_COUNT) {
pasition._splitCurves(curves, pasition.MIM_CURVES_COUNT - lenA)
pasition._splitCurves(clonePathB[index], pasition.MIM_CURVES_COUNT - lenB)
} else {
pasition._splitCurves(curves, lenB - lenA)
}
}
})
clonePathA.forEach(function (curves, index) {
clonePathA[index] = sortCurves(curves, clonePathB[index])
})
return [clonePathA, clonePathB]
}
pasition._lerp = function (pathA, pathB, t) {
let shapes = []
pathA.forEach(function (curves, index) {
let newCurves = []
curves.forEach(function (curve, curveIndex) {
newCurves.push(pasition.lerpCurve(curve, pathB[index][curveIndex], t))
})
shapes.push(newCurves)
})
return shapes
}
let lastTime = 0;
const requestAnimationFrame = function(callback) {
let currTime = new Date().getTime()
let timeToCall = Math.max(0, 16 - (currTime - lastTime))
let id = setTimeout(function() { callback(currTime + timeToCall); },
timeToCall)
lastTime = currTime + timeToCall
return id
};
const cancelAnimationFrame = function(id) {
clearTimeout(id)
}
pasition.animate = function (option) {
let pathA = pasition.path2shapes(option.from)
let pathB = pasition.path2shapes(option.to)
let pathArr = pasition._preprocessing(pathA,pathB)
let beginTime = new Date(),
end = option.end || function () {
},
progress = option.progress || function () {
},
begin = option.begin || function () {
},
easing = option.easing || function (v) {
return v
},
tickId = null,
outShape = null,
duration = option.duration
begin(pathA)
let tick = function () {
let dt = new Date() - beginTime
if (dt >= duration) {
outShape = pathB
progress(outShape, 1)
end(outShape)
cancelAnimationFrame(tickId)
return
}
let percent = easing(dt / duration)
outShape = pasition._lerp(pathArr[0], pathArr[1], percent)
progress(outShape, percent)
tickId = requestAnimationFrame(tick)
}
tick()
}
export default pasition

View File

@ -0,0 +1,159 @@
function shapeBox(shape) {
let minX=shape[0][0], minY=shape[0][1], maxX=minX, maxY=minY
shape.forEach(curve=> {
let x1 = curve[0],
x2 = curve[2],
x3 = curve[4],
x4 = curve[6],
y1 = curve[1],
y2 = curve[3],
y3 = curve[5],
y4 = curve[7]
minX = Math.min(minX, x1, x2, x3, x4)
minY = Math.min(minY, y1, y2, y3, y4)
maxX = Math.max(maxX, x1, x2, x3, x4)
maxY = Math.max(maxY, y1, y2, y3, y4)
})
return [minX, minY, maxX, maxY]
}
function boxDistance(boxA, boxB){
return Math.sqrt(Math.pow( boxA[0] - boxB[0],2)+Math.pow(boxA[1]-boxB[1],2))+ Math.sqrt(Math.pow( boxA[2] - boxB[2],2)+Math.pow(boxA[3]-boxB[3],2))
}
function curveDistance(curveA,curveB) {
let x1 = curveA[0],
x2 = curveA[2],
x3 = curveA[4],
x4 = curveA[6],
y1 = curveA[1],
y2 = curveA[3],
y3 = curveA[5],
y4 = curveA[7],
xb1 = curveB[0],
xb2 = curveB[2],
xb3 = curveB[4],
xb4 = curveB[6],
yb1 = curveB[1],
yb2 = curveB[3],
yb3 = curveB[5],
yb4 = curveB[7]
return Math.sqrt(Math.pow(xb1 - x1, 2) + Math.pow(yb1 - y1, 2)) +
Math.sqrt(Math.pow(xb2 - x2, 2) + Math.pow(yb2 - y2, 2)) +
Math.sqrt(Math.pow(xb3 - x3, 2) + Math.pow(yb3 - y3, 2)) +
Math.sqrt(Math.pow(xb4 - x4, 2) + Math.pow(yb4 - y4, 2))
}
function sortCurves(curvesA, curvesB){
let arrList = permuteCurveNum(curvesA.length)
let list = []
arrList.forEach(arr => {
let distance = 0
let i = 0
arr.forEach(index=> {
distance += curveDistance(curvesA[index], curvesB[i++])
})
list.push({index: arr, distance: distance})
})
list.sort(function(a,b){
return a.distance - b.distance
})
let result = []
list[0].index.forEach(index=>{
result.push(curvesA[index])
})
return result
}
function sort(pathA, pathB){
let arrList = permuteNum(pathA.length)
let list = []
arrList.forEach(arr => {
let distance = 0
arr.forEach(index=> {
distance += boxDistance(shapeBox(pathA[index]), shapeBox(pathB[index]))
})
list.push({index: arr, distance: distance})
})
list.sort(function(a,b){
return a.distance - b.distance
})
let result = []
list[0].index.forEach(index=>{
result.push(pathA[index])
})
return result
}
function permuteCurveNum(num) {
let arr = []
for (let i = 0; i < num; i++) {
let indexArr = []
for (let j = 0; j < num; j++) {
let index = j + i
if (index > num - 1)index -= num
indexArr[index] = j
}
arr.push(indexArr)
}
return arr
}
function permuteNum(num) {
let arr = []
for (let i = 0; i < num; i++) {
arr.push(i)
}
return permute(arr)
}
function permute(input) {
var permArr = [],
usedChars = []
function main(input){
var i, ch
for (i = 0; i < input.length; i++) {
ch = input.splice(i, 1)[0]
usedChars.push(ch)
if (input.length == 0) {
permArr.push(usedChars.slice())
}
main(input)
input.splice(i, 0, ch)
usedChars.pop()
}
return permArr
}
return main(input)
}
export {
sort,
sortCurves
}

View File

@ -0,0 +1,57 @@
//https://github.com/jkroso/parse-svg-path/blob/master/index.js
/**
* expected argument lengths
* @type {Object}
*/
var length = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }
/**
* segment pattern
* @type {RegExp}
*/
var segment = /([astvzqmhlc])([^astvzqmhlc]*)/gi
/**
* parse an svg path data string. Generates an Array
* of commands where each command is an Array of the
* form `[command, arg1, arg2, ...]`
*
* @param {String} path
* @return {Array}
*/
function parse(path) {
var data = []
path.replace(segment, function(_, command, args) {
var type = command.toLowerCase()
args = parseValues(args)
// overloaded moveTo
if (type == 'm' && args.length > 2) {
data.push([command].concat(args.splice(0, 2)))
type = 'l'
command = command == 'm' ? 'l' : 'L'
}
while (true) {
if (args.length == length[type]) {
args.unshift(command)
return data.push(args)
}
if (args.length < length[type]) throw new Error('malformed path data')
data.push([command].concat(args.splice(0, length[type])))
}
})
return data
}
var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/gi
function parseValues(args) {
var numbers = args.match(number)
return numbers ? numbers.map(Number) : []
}
export default parse

View File

@ -0,0 +1,112 @@
// https://src.chromium.org/viewvc/blink/trunk/Source/core/svg/SVGPathParser.cpp?revision=195686#l225
// https://github.com/adobe-webplatform/Snap.svg/blob/master/src/path.js#L752
const math = Math
const PI = math.PI
const abs = math.abs
function cacher(fn) {
const cache = {}
return function () {
const key = [...arguments].join('-')
if (cache.hasOwnProperty(key)) return cache[key]
const result = fn.apply(null, arguments)
cache[key] = result
return result
}
}
function a2c(x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
// for more information of where this math came from visit:
// http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
var _120 = PI * 120 / 180,
rad = PI / 180 * (+angle || 0),
res = [],
xy,
rotate = cacher(function (x, y, rad) {
var X = x * math.cos(rad) - y * math.sin(rad),
Y = x * math.sin(rad) + y * math.cos(rad);
return {x: X, y: Y};
});
if (!rx || !ry) {
return [x1, y1, x2, y2, x2, y2];
}
if (!recursive) {
xy = rotate(x1, y1, -rad);
x1 = xy.x;
y1 = xy.y;
xy = rotate(x2, y2, -rad);
x2 = xy.x;
y2 = xy.y;
var cos = math.cos(PI / 180 * angle),
sin = math.sin(PI / 180 * angle),
x = (x1 - x2) / 2,
y = (y1 - y2) / 2;
var h = x * x / (rx * rx) + y * y / (ry * ry);
if (h > 1) {
h = math.sqrt(h);
rx = h * rx;
ry = h * ry;
}
var rx2 = rx * rx,
ry2 = ry * ry,
k = (large_arc_flag == sweep_flag ? -1 : 1) *
math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
cx = k * rx * y / ry + (x1 + x2) / 2,
cy = k * -ry * x / rx + (y1 + y2) / 2,
f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
f2 = math.asin(((y2 - cy) / ry).toFixed(9));
f1 = x1 < cx ? PI - f1 : f1;
f2 = x2 < cx ? PI - f2 : f2;
f1 < 0 && (f1 = PI * 2 + f1);
f2 < 0 && (f2 = PI * 2 + f2);
if (sweep_flag && f1 > f2) {
f1 = f1 - PI * 2;
}
if (!sweep_flag && f2 > f1) {
f2 = f2 - PI * 2;
}
} else {
f1 = recursive[0];
f2 = recursive[1];
cx = recursive[2];
cy = recursive[3];
}
var df = f2 - f1;
if (abs(df) > _120) {
var f2old = f2,
x2old = x2,
y2old = y2;
f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
x2 = cx + rx * math.cos(f2);
y2 = cy + ry * math.sin(f2);
res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
}
df = f2 - f1;
var c1 = math.cos(f1),
s1 = math.sin(f1),
c2 = math.cos(f2),
s2 = math.sin(f2),
t = math.tan(df / 4),
hx = 4 / 3 * rx * t,
hy = 4 / 3 * ry * t,
m1 = [x1, y1],
m2 = [x1 + hx * s1, y1 - hy * c1],
m3 = [x2 + hx * s2, y2 - hy * c2],
m4 = [x2, y2];
m2[0] = 2 * m1[0] - m2[0];
m2[1] = 2 * m1[1] - m2[1];
if (recursive) {
return [m2, m3, m4].concat(res);
} else {
res = [m2, m3, m4].concat(res).join().split(",");
var newres = [];
for (var i = 0, ii = res.length; i < ii; i++) {
newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
}
return newres;
}
}
export default a2c

View File

@ -0,0 +1,97 @@
import option from './stage-propagation-tag'
class EventDispatcher {
constructor() {
this._listeners = null
this._captureListeners = null
}
addEventListener(type, listener, useCapture) {
var listeners
if (useCapture) {
listeners = this._captureListeners = this._captureListeners || {}
} else {
listeners = this._listeners = this._listeners || {}
}
var arr = listeners[type]
if (arr) {
this.removeEventListener(type, listener, useCapture)
}
arr = listeners[type] // remove may have deleted the array
if (!arr) {
listeners[type] = [listener]
} else {
arr.push(listener)
}
return listener
}
removeEventListener(type, listener, useCapture) {
var listeners = useCapture ? this._captureListeners : this._listeners
if (!listeners) {
return
}
var arr = listeners[type]
if (!arr) {
return
}
arr.every((item, index) => {
if (item === listener) {
arr.splice(index, 1)
return false
}
return true
})
}
on(type, listener, useCapture) {
this.addEventListener(type, listener, useCapture)
}
off(type, listener, useCapture) {
this.removeEventListener(type, listener, useCapture)
}
dispatchEvent(evt) {
option.stagePropagationStopped[evt.type] = false
var top = this,
list = [top]
while (top.parent) {
list.push((top = top.parent))
}
var i,
l = list.length
// capture & atTarget
for (i = l - 1; i >= 0 && !evt.propagationStopped; i--) {
list[i]._dispatchEvent(evt, 0)
}
// bubbling
for (i = 0; i < l && !evt.propagationStopped; i++) {
list[i]._dispatchEvent(evt, 1)
}
}
_dispatchEvent(evt, type) {
evt.target = this
if (this._captureListeners && type === 0) {
let cls = this._captureListeners[evt.type]
cls &&
cls.forEach(fn => {
fn.call(this, evt)
})
}
if (this._listeners && type === 1) {
let ls = this._listeners[evt.type]
ls &&
ls.forEach(fn => {
fn.call(this, evt)
})
}
}
}
export default EventDispatcher

View File

@ -0,0 +1,21 @@
import option from './stage-propagation-tag'
class Event {
constructor() {
this.propagationStopped = false
this.stageX = null
this.stageY = null
this.pureEvent = null
}
stopPropagation() {
option.stagePropagationStopped[this.type] = true
this.propagationStopped = true
}
preventDefault() {
this.pureEvent.preventDefault()
}
}
export default Event

View File

@ -0,0 +1,564 @@
const mt = function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./src/applyToPoint.js
/**
* Calculate a point transformed with an affine matrix
* @param matrix Affine matrix
* @param point Point
* @returns {{x: number, y: number} | Array} Point
*/
function applyToPoint(matrix, point) {
return Array.isArray(point) ? [matrix.a * point[0] + matrix.c * point[1] + matrix.e, matrix.b * point[0] + matrix.d * point[1] + matrix.f] : {
x: matrix.a * point.x + matrix.c * point.y + matrix.e,
y: matrix.b * point.x + matrix.d * point.y + matrix.f
};
}
/**
* Calculate an array of points transformed with an affine matrix
* @param matrix Affine matrix
* @param points Array of points
* @returns {array} Array of points
*/
function applyToPoints(matrix, points) {
return points.map(function (point) {
return applyToPoint(matrix, point);
});
}
// CONCATENATED MODULE: ./src/fromObject.js
/**
* Extract an affine matrix from an object that contains a,b,c,d,e,f keys
* Each value could be a float or a string that contains a float
* @param object
* @return {{a: *, b: *, c: *, e: *, d: *, f: *}}}
*/
function fromObject(object) {
return {
a: parseFloat(object.a),
b: parseFloat(object.b),
c: parseFloat(object.c),
d: parseFloat(object.d),
e: parseFloat(object.e),
f: parseFloat(object.f)
};
}
// CONCATENATED MODULE: ./src/fromString.js
/**
* @ignore
* @type {RegExp}
*/
var matrixRegex = /^matrix\(\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*\)$/i;
/**
* Parse a string matrix formatted as matrix(a,b,c,d,e,f)
* @param string String with a matrix
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function fromString(string) {
var parsed = string.match(matrixRegex);
if (parsed === null || parsed.length < 7) throw new Error("'" + string + "' is not a matrix");
return {
a: parseFloat(parsed[1]),
b: parseFloat(parsed[2]),
c: parseFloat(parsed[3]),
d: parseFloat(parsed[4]),
e: parseFloat(parsed[5]),
f: parseFloat(parsed[6])
};
}
// CONCATENATED MODULE: ./src/identity.js
/**
* Identity matrix
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function identity() {
return {
a: 1,
c: 0,
e: 0,
b: 0,
d: 1,
f: 0
};
}
// CONCATENATED MODULE: ./src/inverse.js
/**
* Calculate a matrix that is the inverse of the provided matrix
* @param matrix Affine matrix
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function inverse(matrix) {
// http://www.wolframalpha.com/input/?i=Inverse+%5B%7B%7Ba,c,e%7D,%7Bb,d,f%7D,%7B0,0,1%7D%7D%5D
var a = matrix.a,
b = matrix.b,
c = matrix.c,
d = matrix.d,
e = matrix.e,
f = matrix.f;
var denom = a * d - b * c;
return {
a: d / denom,
b: b / -denom,
c: c / -denom,
d: a / denom,
e: (d * e - c * f) / -denom,
f: (b * e - a * f) / denom
};
}
// CONCATENATED MODULE: ./src/isAffineMatrix.js
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var isNumeric = function isNumeric(n) {
return typeof n === 'number' && !isNaN(n) && isFinite(n);
};
var isObject = function isObject(obj) {
return obj != null && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object';
};
/**
* Check if the object contain an affine matrix
* @param object
* @return {boolean}
*/
function isAffineMatrix(object) {
return isObject(object) && object.hasOwnProperty('a') && isNumeric(object.a) && object.hasOwnProperty('b') && isNumeric(object.b) && object.hasOwnProperty('c') && isNumeric(object.c) && object.hasOwnProperty('d') && isNumeric(object.d) && object.hasOwnProperty('e') && isNumeric(object.e) && object.hasOwnProperty('f') && isNumeric(object.f);
}
// CONCATENATED MODULE: ./src/utils.js
function isUndefined(val) {
return typeof val === 'undefined';
}
// CONCATENATED MODULE: ./src/translate.js
/**
* Calculate a translate matrix
* @param tx Translation on axis x
* @param [ty = 0] Translation on axis y
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function translate(tx) {
var ty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
return {
a: 1,
c: 0,
e: tx,
b: 0,
d: 1,
f: ty
};
}
// CONCATENATED MODULE: ./src/transform.js
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
/**
* Merge multiple matrices into one
* @param matrices {...object} list of matrices
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function transform() {
for (var _len = arguments.length, matrices = Array(_len), _key = 0; _key < _len; _key++) {
matrices[_key] = arguments[_key];
}
matrices = Array.isArray(matrices[0]) ? matrices[0] : matrices;
var multiply = function multiply(m1, m2) {
return {
a: m1.a * m2.a + m1.c * m2.b,
c: m1.a * m2.c + m1.c * m2.d,
e: m1.a * m2.e + m1.c * m2.f + m1.e,
b: m1.b * m2.a + m1.d * m2.b,
d: m1.b * m2.c + m1.d * m2.d,
f: m1.b * m2.e + m1.d * m2.f + m1.f
};
};
switch (matrices.length) {
case 0:
throw new Error('no matrices provided');
case 1:
return matrices[0];
case 2:
return multiply(matrices[0], matrices[1]);
default:
var _matrices = matrices,
_matrices2 = _toArray(_matrices),
m1 = _matrices2[0],
m2 = _matrices2[1],
rest = _matrices2.slice(2);
var m = multiply(m1, m2);
return transform.apply(undefined, [m].concat(_toConsumableArray(rest)));
}
}
/**
* Merge multiple matrices into one (alias of `transform`)
* @param matrices {...object} list of matrices
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function compose() {
return transform.apply(undefined, arguments);
}
// CONCATENATED MODULE: ./src/rotate.js
var cos = Math.cos,
sin = Math.sin,
PI = Math.PI;
/**
* Calculate a rotation matrix
* @param angle Angle in radians
* @param [cx] If (cx,cy) are supplied the rotate is about this point
* @param [cy] If (cx,cy) are supplied the rotate is about this point
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix *
*/
function rotate(angle, cx, cy) {
var cosAngle = cos(angle);
var sinAngle = sin(angle);
var rotationMatrix = {
a: cosAngle,
c: -sinAngle,
e: 0,
b: sinAngle,
d: cosAngle,
f: 0
};
if (isUndefined(cx) || isUndefined(cy)) {
return rotationMatrix;
}
return transform([translate(cx, cy), rotationMatrix, translate(-cx, -cy)]);
}
/**
* Calculate a rotation matrix with a DEG angle
* @param angle Angle in degree
* @param [cx] If (cx,cy) are supplied the rotate is about this point
* @param [cy] If (cx,cy) are supplied the rotate is about this point
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function rotateDEG(angle) {
var cx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
var cy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
return rotate(angle * PI / 180, cx, cy);
}
// CONCATENATED MODULE: ./src/scale.js
/**
* Calculate a scaling matrix
* @param sx Scaling on axis x
* @param [sy = sx] Scaling on axis y (default sx)
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function scale(sx) {
var sy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
if (isUndefined(sy)) sy = sx;
return {
a: sx,
c: 0,
e: 0,
b: 0,
d: sy,
f: 0
};
}
// CONCATENATED MODULE: ./src/shear.js
/**
* Calculate a shear matrix
* @param shx Shear on axis x
* @param shy Shear on axis y
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function shear(shx, shy) {
return {
a: 1,
c: shx,
e: 0,
b: shy,
d: 1,
f: 0
};
}
// CONCATENATED MODULE: ./src/skew.js
// https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skew
var tan = Math.tan;
/**
* Calculate a skew matrix
* @param ax Skew on axis x
* @param ay Skew on axis y
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function skew(ax, ay) {
return {
a: 1,
c: tan(ax),
e: 0,
b: tan(ay),
d: 1,
f: 0
};
}
/**
* Calculate a skew matrix using DEG angles
* @param ax Skew on axis x
* @param ay Skew on axis y
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix
*/
function skewDEG(ax, ay) {
return skew(ax * Math.PI / 180, ay * Math.PI / 180);
}
// CONCATENATED MODULE: ./src/toString.js
/**
* Serialize the matrix to a string that can be used with CSS or SVG
* @param matrix Affine matrix
* @returns {string} String that contains a matrix formatted as matrix(a,b,c,d,e,f)
*/
function toCSS(matrix) {
return toString_toString(matrix);
}
/**
* Serialize the matrix to a string that can be used with CSS or SVG
* @param matrix Affine matrix
* @returns {string} String that contains a matrix formatted as matrix(a,b,c,d,e,f)
*/
function toSVG(matrix) {
return toString_toString(matrix);
}
/**
* Serialize the matrix to a string that can be used with CSS or SVG
* @param matrix Affine matrix
* @returns {string} String that contains a matrix formatted as matrix(a,b,c,d,e,f)
*/
function toString_toString(matrix) {
return "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
}
// CONCATENATED MODULE: ./src/smoothMatrix.js
/**
* Rounds all elements of the given matrix using the given precision
* @param m {{a: number, b: number, c: number, d: number, e: number, f: number}} a matrix to round
* @param [precision] a precision to use for Math.round. Defaults to 10000000000 (meaning which rounds to the 10th digit after the comma).
* @returns {{a: number, b: number, c: number, d: number, e: number, f: number}} the rounded matrix
*/
function smoothMatrix(m) {
var precision = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10000000000;
return {
a: Math.round(m.a * precision) / precision,
b: Math.round(m.b * precision) / precision,
c: Math.round(m.c * precision) / precision,
d: Math.round(m.d * precision) / precision,
e: Math.round(m.e * precision) / precision,
f: Math.round(m.f * precision) / precision
};
}
// CONCATENATED MODULE: ./src/fromTriangles.js
/**
* Returns a matrix that transforms a triangle t1 into another triangle t2, or throws an exception if it is impossible.
* @param t1 {Array.<{x: number, y: number}> | Array.<Array<number>>} an array of points containing the three points for the first triangle
* @param t2 {Array.<{x: number, y: number}> | Array.<Array<number>>} an array of points containing the three points for the second triangle
* @returns {{a: number, b: number, c: number, e: number, d: number, f: number}} Affine matrix which transforms t1 to t2
* @throws Exception if the matrix becomes not invertible
*/
function fromTriangles(t1, t2) {
// point p = first point of the triangle
var px1 = t1[0].x != null ? t1[0].x : t1[0][0];
var py1 = t1[0].y != null ? t1[0].y : t1[0][1];
var px2 = t2[0].x != null ? t2[0].x : t2[0][0];
var py2 = t2[0].y != null ? t2[0].y : t2[0][1];
// point q = second point of the triangle
var qx1 = t1[1].x != null ? t1[1].x : t1[1][0];
var qy1 = t1[1].y != null ? t1[1].y : t1[1][1];
var qx2 = t2[1].x != null ? t2[1].x : t2[1][0];
var qy2 = t2[1].y != null ? t2[1].y : t2[1][1];
// point r = third point of the triangle
var rx1 = t1[2].x != null ? t1[2].x : t1[2][0];
var ry1 = t1[2].y != null ? t1[2].y : t1[2][1];
var rx2 = t2[2].x != null ? t2[2].x : t2[2][0];
var ry2 = t2[2].y != null ? t2[2].y : t2[2][1];
var r1 = {
a: px1 - rx1,
b: py1 - ry1,
c: qx1 - rx1,
d: qy1 - ry1,
e: rx1,
f: ry1
};
var r2 = {
a: px2 - rx2,
b: py2 - ry2,
c: qx2 - rx2,
d: qy2 - ry2,
e: rx2,
f: ry2
};
var inverseR1 = inverse(r1);
var affineMatrix = transform([r2, inverseR1]);
// round the matrix elements to smooth the finite inversion
return smoothMatrix(affineMatrix);
}
// CONCATENATED MODULE: ./src/index.js
/* concated harmony reexport applyToPoint */__webpack_require__.d(__webpack_exports__, "applyToPoint", function() { return applyToPoint; });
/* concated harmony reexport applyToPoints */__webpack_require__.d(__webpack_exports__, "applyToPoints", function() { return applyToPoints; });
/* concated harmony reexport fromObject */__webpack_require__.d(__webpack_exports__, "fromObject", function() { return fromObject; });
/* concated harmony reexport fromString */__webpack_require__.d(__webpack_exports__, "fromString", function() { return fromString; });
/* concated harmony reexport identity */__webpack_require__.d(__webpack_exports__, "identity", function() { return identity; });
/* concated harmony reexport inverse */__webpack_require__.d(__webpack_exports__, "inverse", function() { return inverse; });
/* concated harmony reexport isAffineMatrix */__webpack_require__.d(__webpack_exports__, "isAffineMatrix", function() { return isAffineMatrix; });
/* concated harmony reexport rotate */__webpack_require__.d(__webpack_exports__, "rotate", function() { return rotate; });
/* concated harmony reexport rotateDEG */__webpack_require__.d(__webpack_exports__, "rotateDEG", function() { return rotateDEG; });
/* concated harmony reexport scale */__webpack_require__.d(__webpack_exports__, "scale", function() { return scale; });
/* concated harmony reexport shear */__webpack_require__.d(__webpack_exports__, "shear", function() { return shear; });
/* concated harmony reexport skew */__webpack_require__.d(__webpack_exports__, "skew", function() { return skew; });
/* concated harmony reexport skewDEG */__webpack_require__.d(__webpack_exports__, "skewDEG", function() { return skewDEG; });
/* concated harmony reexport toCSS */__webpack_require__.d(__webpack_exports__, "toCSS", function() { return toCSS; });
/* concated harmony reexport toSVG */__webpack_require__.d(__webpack_exports__, "toSVG", function() { return toSVG; });
/* concated harmony reexport toString */__webpack_require__.d(__webpack_exports__, "toString", function() { return toString_toString; });
/* concated harmony reexport transform */__webpack_require__.d(__webpack_exports__, "transform", function() { return transform; });
/* concated harmony reexport compose */__webpack_require__.d(__webpack_exports__, "compose", function() { return compose; });
/* concated harmony reexport translate */__webpack_require__.d(__webpack_exports__, "translate", function() { return translate; });
/* concated harmony reexport fromTriangles */__webpack_require__.d(__webpack_exports__, "fromTriangles", function() { return fromTriangles; });
/* concated harmony reexport smoothMatrix */__webpack_require__.d(__webpack_exports__, "smoothMatrix", function() { return smoothMatrix; });
/***/ })
/******/ ]);
}
//# sourceMappingURL=transformation-matrix.js.map
export default mt()

View File

@ -0,0 +1,154 @@
const DEG_TO_RAD = 0.017453292519943295
const PI_2 = Math.PI * 2
class Matrix2D {
constructor(a, b, c, d, tx, ty) {
this.a = a == null ? 1 : a
this.b = b || 0
this.c = c || 0
this.d = d == null ? 1 : d
this.tx = tx || 0
this.ty = ty || 0
return this
}
identity() {
this.a = this.d = 1
this.b = this.c = this.tx = this.ty = 0
return this
}
appendTransform(
x,
y,
scaleX,
scaleY,
rotation,
skewX,
skewY,
originX,
originY
) {
if (rotation % 360) {
var r = rotation * DEG_TO_RAD
var cos = Math.cos(r)
var sin = Math.sin(r)
} else {
cos = 1
sin = 0
}
if (skewX || skewY) {
skewX *= DEG_TO_RAD
skewY *= DEG_TO_RAD
this.append(
Math.cos(skewY),
Math.sin(skewY),
-Math.sin(skewX),
Math.cos(skewX),
x,
y
)
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, 0, 0)
} else {
this.append(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y)
}
if (originX || originY) {
this.tx -= originX * this.a + originY * this.c
this.ty -= originX * this.b + originY * this.d
}
return this
}
append(a, b, c, d, tx, ty) {
var a1 = this.a
var b1 = this.b
var c1 = this.c
var d1 = this.d
this.a = a * a1 + b * c1
this.b = a * b1 + b * d1
this.c = c * a1 + d * c1
this.d = c * b1 + d * d1
this.tx = tx * a1 + ty * c1 + this.tx
this.ty = tx * b1 + ty * d1 + this.ty
return this
}
initialize(a, b, c, d, tx, ty) {
this.a = a
this.b = b
this.c = c
this.d = d
this.tx = tx
this.ty = ty
return this
}
setValues(a, b, c, d, tx, ty) {
this.a = a == null ? 1 : a
this.b = b || 0
this.c = c || 0
this.d = d == null ? 1 : d
this.tx = tx || 0
this.ty = ty || 0
return this
}
invert() {
let a1 = this.a
let b1 = this.b
let c1 = this.c
let d1 = this.d
let tx1 = this.tx
let n = a1 * d1 - b1 * c1
this.a = d1 / n
this.b = -b1 / n
this.c = -c1 / n
this.d = a1 / n
this.tx = (c1 * this.ty - d1 * tx1) / n
this.ty = -(a1 * this.ty - b1 * tx1) / n
return this
}
copy(matrix) {
return this.setValues(
matrix.a,
matrix.b,
matrix.c,
matrix.d,
matrix.tx,
matrix.ty
)
}
}
Matrix2D.decompose = function(a, b, c, d, tx, ty, transform) {
const skewX = -Math.atan2(-c, d)
const skewY = Math.atan2(b, a)
const delta = Math.abs(skewX + skewY)
if (delta < 0.00001 || Math.abs(PI_2 - delta) < 0.00001) {
transform.rotation = skewY
if (a < 0 && d >= 0) {
transform.rotation += transform.rotation <= 0 ? Math.PI : -Math.PI
}
transform.skewX = transform.skewY = 0
} else {
transform.rotation = 0
transform.skewX = skewX
transform.skewY = skewY
}
// next set scale
transform.scaleX = Math.sqrt(a * a + b * b)
transform.scaleY = Math.sqrt(c * c + d * d)
// next set position
transform.x = tx
transform.y = ty
}
export default Matrix2D

View File

@ -0,0 +1,57 @@
// https://github.com/jkroso/parse-svg-path/blob/master/index.js
/**
* expected argument lengths
* @type {Object}
*/
var length = { a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0 }
/**
* segment pattern
* @type {RegExp}
*/
var segment = /([astvzqmhlc])([^astvzqmhlc]*)/gi
/**
* parse an svg path data string. Generates an Array
* of commands where each command is an Array of the
* form `[command, arg1, arg2, ...]`
*
* @param {String} path
* @return {Array}
*/
function parse(path) {
var data = []
path.replace(segment, function(_, command, args) {
var type = command.toLowerCase()
args = parseValues(args)
// overloaded moveTo
if (type === 'm' && args.length > 2) {
data.push([command].concat(args.splice(0, 2)))
type = 'l'
command = command === 'm' ? 'l' : 'L'
}
while (true) {
if (args.length === length[type]) {
args.unshift(command)
return data.push(args)
}
if (args.length < length[type]) throw new Error('malformed path data')
data.push([command].concat(args.splice(0, length[type])))
}
})
return data
}
var number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/gi
function parseValues(args) {
var numbers = args.match(number)
return numbers ? numbers.map(Number) : []
}
export default parse

View File

@ -0,0 +1,3 @@
export default {
stagePropagationStopped: {}
}

View File

@ -0,0 +1,9 @@
var UID = {}
UID._nextID = 0
UID.get = function() {
return UID._nextID++
}
export default UID

View File

@ -0,0 +1,79 @@
import DisplayObject from './display-object.js'
import util from '../../common/util'
class Bitmap extends DisplayObject {
constructor(img, onLoad) {
super()
if (typeof img === 'string') {
if (Bitmap.cache[img]) {
if (util.isWeapp) {
this.img = Bitmap.cache[img].img
this.rect = [0, 0, Bitmap.cache[img].width, Bitmap.cache[img].height]
this.width = this.rect[2]
this.height = this.rect[3]
} else {
this.img = Bitmap.cache[img]
this.rect = [0, 0, this.img.width, this.img.height]
this.width = this.img.width
this.height = this.img.height
}
onLoad && onLoad.call(this)
} else if (util.isWeapp) {
util.getImageInWx(img, result => {
this.img = result.img
if (!this.rect) {
this.rect = [0, 0, result.width, result.height]
}
this.width = result.width
this.height = result.height
onLoad && onLoad.call(this)
Bitmap.cache[img] = result
})
} else {
this.img = util.isWegame ? wx.createImage() : new window.Image()
this.img.onload = () => {
if (!this.rect) {
this.rect = [0, 0, this.img.width, this.img.height]
}
this.width = this.img.width
this.height = this.img.height
onLoad && onLoad.call(this)
Bitmap.cache[img] = this.img
}
this.img.src = img
}
} else {
this.img = img
this.rect = [0, 0, img.width, img.height]
this.width = img.width
this.height = img.height
Bitmap.cache[img.src] = img
}
}
clone() {
// 复制完img宽度0所以直接传字符串
const bitmap = new Bitmap(
typeof this.img === 'string' ? this.img : this.img.src
)
bitmap.x = this.x
bitmap.y = this.y
bitmap.scaleX = this.scaleX
bitmap.scaleY = this.scaleY
bitmap.rotation = this.rotation
bitmap.skewX = this.skewX
bitmap.skewY = this.skewY
bitmap.originX = this.originX
bitmap.originY = this.originY
bitmap.width = this.width
bitmap.height = this.height
bitmap.cursor = this.cursor
return bitmap
}
}
Bitmap.cache = {}
export default Bitmap

View File

@ -0,0 +1,245 @@
import Matrix2D from '../base/matrix2d.js'
import EventDispatcher from '../base/event-dispatcher'
import UID from '../base/uid.js'
class DisplayObject extends EventDispatcher {
constructor() {
super()
this.alpha = this.complexAlpha = this.scaleX = this.scaleY = 1
this.x = this.y = this.rotation = this.skewX = this.skewY = this.originX = this.originY = 0
this.cursor = null
this.visible = true
this._matrix = new Matrix2D()
this._hitMatrix = new Matrix2D()
this.id = UID.get()
this.clipGraphics = null
this.clipRuleNonzero = true
this.fixed = false
this.shadow = null
this.compositeOperation = null
this.absClipGraphics = null
this.absClipRuleNonzero = true
this.cacheUpdating = false
this.boundsX = 0
this.boundsY = 0
try {
Object.defineProperties(this, {
stage: { get: this._getStage },
scale: {
get: function() {
return this.scaleX
},
set: function(scale) {
this.scaleX = this.scaleY = scale
}
}
})
} catch (e) {}
//不推荐,使用 boundsX、boundsX、width、height代替
this.hitBox = null
}
isVisible() {
return (
this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0
)
}
initAABB() {
if (
(this.width === undefined || this.height === undefined) &&
!this.hitBox
) {
return
}
let x,
y,
width = this.width,
height = this.height,
mtx = this._matrix,
tx = mtx.tx,
ty = mtx.ty
if (this.hitBox) {
width = this.hitBox[2]
height = this.hitBox[3]
tx = this.hitBox[0] * mtx.a + this.hitBox[1] * mtx.c + tx
ty = this.hitBox[0] * mtx.b + this.hitBox[1] * mtx.d + ty
}
if (this.boundsX || this.boundsY) {
tx = this.boundsX * mtx.a + this.boundsY * mtx.c + tx
ty = this.boundsX * mtx.b + this.boundsY * mtx.d + ty
}
let xA = width * mtx.a,
xB = width * mtx.b,
yC = height * mtx.c,
yD = height * mtx.d,
minX = tx,
maxX = tx,
minY = ty,
maxY = ty
if ((x = xA + tx) < minX) {
minX = x
} else if (x > maxX) {
maxX = x
}
if ((x = xA + yC + tx) < minX) {
minX = x
} else if (x > maxX) {
maxX = x
}
if ((x = yC + tx) < minX) {
minX = x
} else if (x > maxX) {
maxX = x
}
if ((y = xB + ty) < minY) {
minY = y
} else if (y > maxY) {
maxY = y
}
if ((y = xB + yD + ty) < minY) {
minY = y
} else if (y > maxY) {
maxY = y
}
if ((y = yD + ty) < minY) {
minY = y
} else if (y > maxY) {
maxY = y
}
this.AABB = [minX, minY, maxX - minX, maxY - minY]
this.rectPoints = [
{
x: tx,
y: ty
},
{
x: xA + tx,
y: xB + ty
},
{
x: xA + yC + tx,
y: xB + yD + ty
},
{
x: yC + tx,
y: yD + ty
}
]
}
destroy() {
this.parent.remove(this)
}
hover(over, out, move) {
this.on('mouseover', over)
this.on('mouseout', out)
move && this.on('mousemove', move)
}
// https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/clip
clip(graphics, notClipRuleNonzero) {
this.clipGraphics = graphics
this.clipRuleNonzero = !notClipRuleNonzero
}
unclip() {
this.clipGraphics = null
}
absClip(graphics, notClipRuleNonzero) {
this.absClipGraphics = graphics
this.absClipRuleNonzero = !notClipRuleNonzero
}
unAbsClip() {
this.absClipGraphics = null
}
cache(x, y, width, height, scale, cacheUpdating) {
this._cacheData = {
x: x || 0,
y: y || 0,
width: width || this.width,
height: height || this.height,
scale: scale || 1
}
this.cacheUpdating = cacheUpdating
if (!this.cacheCanvas) {
if (typeof wx !== 'undefined' && wx.createCanvas) {
this.cacheCanvas = wx.createCanvas()
} else {
this.cacheCanvas = document.createElement('canvas')
}
this.cacheCtx = this.cacheCanvas.getContext('2d')
}
this.cacheCanvas.width = this._cacheData.width * this._cacheData.scale
this.cacheCanvas.height = this._cacheData.height * this._cacheData.scale
// debug cache canvas
// this.cacheCtx.fillRect(0,0,1000,1000)
this._readyToCache = true
}
uncache() {
this.cacheCanvas = null
}
filter(filterName, filterBox) {
filterBox = Object.assign(
{},
{
x: 0,
y: 0,
width: this.width,
height: this.height
},
filterBox
)
this.cache(filterBox.x, filterBox.y, filterBox.width, filterBox.height)
this._readyToFilter = true
this._filterName = filterName
}
setTransform(x, y, scaleX, scaleY, rotation, skewX, skewY, originX, originY) {
this.x = x || 0
this.y = y || 0
this.scaleX = scaleX == null ? 1 : scaleX
this.scaleY = scaleY == null ? 1 : scaleY
this.rotation = rotation || 0
this.skewX = skewX || 0
this.skewY = skewY || 0
this.originX = originX || 0
this.originY = originY || 0
}
setMatrix(a, b, c, d, tx, ty) {
Matrix2D.decompose(a, b, c, d, tx, ty, this)
}
unfilter() {
this.uncache()
}
_getStage() {
var o = this
while (o.parent) {
o = o.parent
}
if (o.___instanceof === 'Stage') {
return o
}
return null
}
}
export default DisplayObject

View File

@ -0,0 +1,142 @@
import Group from '../group'
import Text from '../text'
import RoundedRect from '../shape/rounded-rect'
import Bitmap from '../bitmap'
/*
Options
font:
text:
textColor:
image: [path, width, height]
bgColor:
bgImage: [path, width, height]
borderRadius:
borderColor:
*/
class Button extends Group {
constructor(option) {
super()
this.width = option.width
this.height = option.height
this.x = option.x
this.y = option.y
let textHeight = 0
var textGroup
if (option.text) {
textGroup = new Group()
this.text = new Text(option.text, {
font: option.font,
color: option.color
})
const textWidth = this.text.getWidth()
if (textWidth > option.width) {
const step = Math.round(
(option.text.length * option.width) / textWidth / 2
)
const textList = this.stringSplit(option.text, step)
const lineHeight = option.lineHeight || 12
textHeight = textList.length * lineHeight + 6
textList.forEach((text, index) => {
this.text = new Text(text, {
font: option.font,
color: option.color
})
this.text.x =
option.width / 2 -
(this.text.getWidth() / 2) * this.text.scaleX +
(option.textX || 0)
this.text.y =
Math.max(textHeight, option.height) / 2 -
10 +
5 * this.text.scaleY +
(option.textY || 0) +
index * 12 -
textHeight / 2 +
lineHeight / 2
textGroup.add(this.text)
})
} else {
this.text.x =
option.width / 2 -
(this.text.getWidth() / 2) * this.text.scaleX +
(option.textX || 0)
this.text.y =
option.height / 2 - 10 + 5 * this.text.scaleY + (option.textY || 0)
textGroup.add(this.text)
}
}
if (option.bgImage) {
var ratio = SCALE_RATIO
let bitmap = new Bitmap(option.bgImage[0])
bitmap.scaleX = ratio
bitmap.scaleY = ratio
bitmap.width = option.bgImage[1]
bitmap.height = option.bgImage[2]
bitmap.x = (this.width - bitmap.width) / 2
bitmap.y = (this.height - bitmap.height) / 2
this.add(bitmap)
} else if (option.bgColor || option.borderColor) {
this.roundedRect = new RoundedRect(
option.width,
option.autoHeight ? Math.max(textHeight, option.height) : option.height,
option.borderRadius,
{
strokeStyle: option.borderColor || 'black',
fillStyle: option.bgColor || '#F5F5F5'
}
)
this.add(this.roundedRect)
}
if (option.image) {
var ratio = SCALE_RATIO
let bitmap = new Bitmap(option.image[0])
bitmap.scaleX = ratio
bitmap.scaleY = ratio
bitmap.width = option.image[1]
bitmap.height = option.image[2]
bitmap.x = (this.width - bitmap.width) / 2
bitmap.y = (this.height - bitmap.height) / 2
this.add(bitmap)
}
if (textGroup) {
this.add(textGroup)
}
}
stringSplit(str, len) {
let arr = [],
offset = 0,
char_length = 0
for (let i = 0; i < str.length; i++) {
let son_str = str.charAt(i)
encodeURI(son_str).length > 2 ? (char_length += 1) : (char_length += 0.5)
if (char_length >= len || (char_length < len && i === str.length - 1)) {
let sub_len = char_length == len ? i + 1 : i
arr.push(
str.substr(
offset,
sub_len -
offset +
(char_length < len && i === str.length - 1 ? 1 : 0)
)
)
offset = i + 1
char_length = 0
}
}
return arr
}
}
export default Button

View File

@ -0,0 +1,181 @@
import DisplayObject from './display-object.js'
const assMap = {
fillStyle: true,
strokeStyle: true,
lineWidth: true,
lineCap: true,
lineDashOffset: true,
lineJoin: true,
miterLimit: true
}
class Graphics extends DisplayObject {
constructor() {
super()
this.cmds = []
this.currentGradient = null
}
clearRect() {
this.cmds.push(['clearRect', arguments])
return this
}
rect() {
this.cmds.push(['rect', arguments])
return this
}
clear() {
this.cmds.length = 0
return this
}
setLineDash() {
this.cmds.push(['setLineDash', arguments])
return this
}
strokeRect() {
this.cmds.push(['strokeRect', arguments])
return this
}
fillRect() {
this.cmds.push(['fillRect', arguments])
return this
}
beginPath() {
this.cmds.push(['beginPath', arguments])
return this
}
arc() {
this.cmds.push(['arc', arguments])
return this
}
closePath() {
this.cmds.push(['closePath', arguments])
return this
}
fillStyle() {
this.cmds.push(['fillStyle', arguments])
return this
}
fill() {
this.cmds.push(['fill', arguments])
return this
}
strokeStyle() {
this.cmds.push(['strokeStyle', arguments])
return this
}
lineWidth() {
this.cmds.push(['lineWidth', arguments])
return this
}
lineCap() {
this.cmds.push(['lineCap', arguments])
return this
}
lineDashOffset() {
this.cmds.push(['lineDashOffset', arguments])
return this
}
lineJoin() {
this.cmds.push(['lineJoin', arguments])
return this
}
miterLimit() {
this.cmds.push(['miterLimit', arguments])
return this
}
stroke() {
this.cmds.push(['stroke', arguments])
return this
}
moveTo() {
this.cmds.push(['moveTo', arguments])
return this
}
lineTo() {
this.cmds.push(['lineTo', arguments])
return this
}
bezierCurveTo() {
this.cmds.push(['bezierCurveTo', arguments])
return this
}
quadraticCurveTo() {
this.cmds.push(['quadraticCurveTo', arguments])
return this
}
createRadialGradient() {
this.cmds.push(['createRadialGradient', arguments])
return this
}
createLinearGradient() {
this.cmds.push(['createLinearGradient', arguments])
return this
}
addColorStop() {
this.cmds.push(['addColorStop', arguments])
return this
}
fillGradient() {
this.cmds.push(['fillGradient'])
return this
}
arcTo() {
this.cmds.push(['arcTo', arguments])
return this
}
render(ctx) {
this.cmds.forEach(cmd => {
const methodName = cmd[0]
if (assMap[methodName]) {
ctx[methodName] = cmd[1][0]
} else if (methodName === 'addColorStop') {
this.currentGradient &&
this.currentGradient.addColorStop(cmd[1][0], cmd[1][1])
} else if (methodName === 'fillGradient') {
ctx.fillStyle = this.currentGradient
} else {
let result = ctx[methodName].apply(
ctx,
Array.prototype.slice.call(cmd[1])
)
if (
methodName === 'createRadialGradient' ||
methodName === 'createLinearGradient'
) {
this.currentGradient = result
}
}
})
}
}
export default Graphics

View File

@ -0,0 +1,74 @@
import DisplayObject from './display-object.js'
class Group extends DisplayObject {
constructor(data) {
super(data)
this.children = []
}
add(child) {
const len = arguments.length
for (let i = 0; i < len; i++) {
const c = arguments[i]
const parent = c.parent
if (parent) {
parent.removeChildAt(parent.children.indexOf(c))
}
this.children.push(c)
c.parent = this
}
}
addChildAt(child, index) {
var par = child.parent
par && par.removeChildAt(par.children.indexOf(child))
child.parent = this
this.children.splice(index, 0, child)
}
removeChildAt(index) {
var child = this.children[index]
if (child) {
child.parent = null
}
this.children.splice(index, 1)
}
replace(current, pre) {
const index = pre.parent.children.indexOf(pre)
this.removeChildAt(index)
this.addChildAt(current, index)
}
remove(child) {
const len = arguments.length
let cLen = this.children.length
for (let i = 0; i < len; i++) {
for (let j = 0; j < cLen; j++) {
if (child.id === this.children[j].id) {
child.parent = null
this.children.splice(j, 1)
j--
cLen--
}
}
}
}
empty() {
this.children.forEach(child => {
child.parent = null
})
this.children.length = 0
}
destroy() {
this.empty()
// Stage does not have a parent
this.parent && super.destroy()
}
}
export default Group

View File

@ -0,0 +1,69 @@
import Shape from './shape'
class ArrowPath extends Shape {
constructor(path, option) {
super()
this.path = path
this.option = Object.assign(
{
strokeStyle: 'black',
lineWidth: 1,
headSize: 10
},
option
)
}
draw() {
const path = this.path
const len = path.length
if (len === 2) {
this.drawArrow(path[0].x, path[0].y, path[1].x, path[1].y, 30)
} else {
this.moveTo(path[0].x, path[0].y)
for (let i = 1; i < len - 1; i++) {
this.lineTo(path[i].x, path[i].y)
}
this.drawArrow(
path[len - 2].x,
path[len - 2].y,
path[len - 1].x,
path[len - 1].y,
30
)
}
//this.stroke()
}
drawArrow(fromX, fromY, toX, toY, theta) {
let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
angle1 = ((angle + theta) * Math.PI) / 180,
angle2 = ((angle - theta) * Math.PI) / 180,
hs = this.option.headSize,
topX = hs * Math.cos(angle1),
topY = hs * Math.sin(angle1),
botX = hs * Math.cos(angle2),
botY = hs * Math.sin(angle2)
let arrowX = fromX - topX,
arrowY = fromY - topY
this.moveTo(arrowX, arrowY)
this.moveTo(fromX, fromY)
this.lineTo(toX, toY)
arrowX = toX + topX
arrowY = toY + topY
this.moveTo(arrowX, arrowY)
this.lineTo(toX, toY)
arrowX = toX + botX
arrowY = toY + botY
this.lineTo(arrowX, arrowY)
// this.strokeStyle(this.option.strokeStyle)
// this.lineWidth(this.option.lineWidth)
}
}
export default ArrowPath

View File

@ -0,0 +1,17 @@
import Shape from './shape'
class Circle extends Shape {
constructor(r, option) {
super()
this.option = option || {}
this.r = r
this._dp = Math.PI * 2
}
draw() {
this.arc(0, 0, this.r, 0, this._dp, false)
}
}
export default Circle

View File

@ -0,0 +1,30 @@
import Shape from './shape'
class Ellipse extends Shape {
constructor(width, height, option) {
super()
this.option = option || {}
this.width = width
this.height = height
}
draw() {
const w = this.width
const h = this.height
const k = 0.5522848
const ox = (w / 2) * k
const oy = (h / 2) * k
const xe = w
const ye = h
const xm = w / 2
const ym = h / 2
this.moveTo(0, ym)
this.bezierCurveTo(0, ym - oy, xm - ox, 0, xm, 0)
this.bezierCurveTo(xm + ox, 0, xe, ym - oy, xe, ym)
this.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye)
this.bezierCurveTo(xm - ox, ye, 0, ym + oy, 0, ym)
}
}
export default Ellipse

View File

@ -0,0 +1,56 @@
import Shape from './shape'
class EquilateralPolygon extends Shape {
constructor(num, r, options) {
super()
this.num = num
this.r = r
this.options = options || {}
this.vertex = []
this.initVertex()
}
initVertex() {
this.vertex.length = []
const num = this.num
const r = this.r
let i, startX, startY, newX, newY
if (num % 2 === 0) {
startX = r * Math.cos((2 * Math.PI * 0) / num)
startY = r * Math.sin((2 * Math.PI * 0) / num)
this.vertex.push([startX, startY])
for (i = 1; i < num; i++) {
newX = r * Math.cos((2 * Math.PI * i) / num)
newY = r * Math.sin((2 * Math.PI * i) / num)
this.vertex.push([newX, newY])
}
} else {
startX = r * Math.cos((2 * Math.PI * 0) / num - Math.PI / 2)
startY = r * Math.sin((2 * Math.PI * 0) / num - Math.PI / 2)
this.vertex.push([startX, startY])
for (i = 1; i < num; i++) {
newX = r * Math.cos((2 * Math.PI * i) / num - Math.PI / 2)
newY = r * Math.sin((2 * Math.PI * i) / num - Math.PI / 2)
this.vertex.push([newX, newY])
}
}
}
draw() {
this.moveTo(this.vertex[0][0], this.vertex[0][1])
for (let i = 1, len = this.vertex.length; i < len; i++) {
this.lineTo(this.vertex[i][0], this.vertex[i][1])
}
this.closePath()
}
}
export default EquilateralPolygon

View File

@ -0,0 +1,19 @@
import Shape from './shape'
class Line extends Shape {
constructor(x1, y1, x2, y2, option) {
super()
this.option = option || {}
this.x1 = x1
this.y1 = y1
this.x2 = x2
this.y2 = y2
}
draw() {
this.moveTo(this.x1, this.y1)
this.lineTo(this.x2, this.y2)
}
}
export default Line

View File

@ -0,0 +1,308 @@
import parse from '../../base/path-parser.js'
import Shape from './shape'
import a2c from '../../base/a2c'
class Path extends Shape {
constructor(d, option) {
super()
this.d = d
option = Object.assign(
{
lineWidth: 1
},
option
)
this.option = option
}
draw() {
const cmds = parse(this.d)
// https://developer.mozilla.org/zh-CN/docs/Web/SVG/Tutorial/Paths
// M = moveto
// L = lineto
// H = horizontal lineto
// V = vertical lineto
// C = curveto
// S = smooth curveto
// Q = quadratic Belzier curve
// T = smooth quadratic Belzier curveto
// A = elliptical Arc 暂时未实现,用贝塞尔拟合椭圆
// Z = closepath
// 以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位(从上一个点开始)。
let preX, preY, curves
// 参考我的 pasition https://github.com/AlloyTeam/pasition/blob/master/src/index.js
for (let j = 0, cmdLen = cmds.length; j < cmdLen; j++) {
let item = cmds[j]
let action = item[0]
let preItem = cmds[j - 1]
switch (action) {
case 'M':
preX = item[1]
preY = item[2]
this.moveTo(preX, preY)
break
case 'L':
preX = item[1]
preY = item[2]
this.lineTo(preX, preY)
break
case 'H':
preX = item[1]
this.lineTo(preX, preY)
break
case 'V':
preY = item[1]
this.lineTo(preX, preY)
break
case 'C':
preX = item[5]
preY = item[6]
this.bezierCurveTo(item[1], item[2], item[3], item[4], preX, preY)
break
case 'S':
if (preItem[0] === 'C' || preItem[0] === 'c') {
this.bezierCurveTo(
preX + preItem[5] - preItem[3],
preY + preItem[6] - preItem[4],
item[1],
item[2],
item[3],
item[4]
)
} else if (preItem[0] === 'S' || preItem[0] === 's') {
this.bezierCurveTo(
preX + preItem[3] - preItem[1],
preY + preItem[4] - preItem[2],
item[1],
item[2],
item[3],
item[4]
)
} else {
this.bezierCurveTo(preX, preY, item[1], item[2], item[3], item[4])
}
preX = item[3]
preY = item[4]
break
case 'Q':
preX = item[3]
preY = item[4]
this.quadraticCurveTo(item[1], item[2], preX, preY)
break
case 'm':
preX += item[1]
preY += item[2]
this.moveTo(preX, preY)
break
case 'l':
preX += item[1]
preY += item[2]
this.lineTo(preX, preY)
break
case 'h':
preX += item[1]
this.lineTo(preX, preY)
break
case 'v':
preY += item[1]
this.lineTo(preX, preY)
break
case 'c':
this.bezierCurveTo(
preX + item[1],
preY + item[2],
preX + item[3],
preY + item[4],
preX + item[5],
preY + item[6]
)
preX = preX + item[5]
preY = preY + item[6]
break
case 's':
if (preItem[0] === 'C' || preItem[0] === 'c') {
this.bezierCurveTo(
preX + preItem[5] - preItem[3],
preY + preItem[6] - preItem[4],
preX + item[1],
preY + item[2],
preX + item[3],
preY + item[4]
)
} else if (preItem[0] === 'S' || preItem[0] === 's') {
this.bezierCurveTo(
preX + preItem[3] - preItem[1],
preY + preItem[4] - preItem[2],
preX + item[1],
preY + item[2],
preX + item[3],
preY + item[4]
)
}
preX += item[3]
preY += item[4]
break
case 'q':
this.quadraticCurveTo(
preX + item[1],
preY + item[2],
item[3] + preX,
item[4] + preY
)
preX += item[3]
preY += item[4]
break
case 'Z':
this.closePath()
break
case 'z':
this.closePath()
break
case 'a':
curves = a2c(
preX,
preY,
item[1],
item[2],
item[3],
item[4],
item[5],
preX + item[6],
preY + item[7]
)
//不能 moveTo ,会导致 closePath 重新设置起点
// this.moveTo(preX, preY)
this.bezierCurveTo(
curves[0],
curves[1],
curves[2],
curves[3],
curves[4],
curves[5]
)
for (let i = 6, len = curves.length; i < len; i += 6) {
this.bezierCurveTo(
curves[i],
curves[i + 1],
curves[i + 2],
curves[i + 3],
curves[i + 4],
curves[i + 5]
)
}
preX = preX + item[6]
preY = preY + item[7]
break
case 'A':
curves = a2c(
preX,
preY,
item[1],
item[2],
item[3],
item[4],
item[5],
item[6],
item[7]
)
//this.moveTo(preX, preY)
this.bezierCurveTo(
curves[0],
curves[1],
curves[2],
curves[3],
curves[4],
curves[5]
)
for (let i = 6, len = curves.length; i < len; i += 6) {
this.bezierCurveTo(
curves[i],
curves[i + 1],
curves[i + 2],
curves[i + 3],
curves[i + 4],
curves[i + 5]
)
}
preX = item[6]
preY = item[7]
break
case 'T':
if (preItem[0] === 'Q' || preItem[0] === 'q') {
preCX = preX + preItem[3] - preItem[1]
preCY = preY + preItem[4] - preItem[2]
this.quadraticCurveTo(preX, preY, preCX, preCY, item[1], item[2])
} else if (preItem[0] === 'T' || preItem[0] === 't') {
this.quadraticCurveTo(
preX,
preY,
preX + preX - preCX,
preY + preY - preCY,
item[1],
item[2]
)
preCX = preX + preX - preCX
preCY = preY + preY - preCY
}
preX = item[1]
preY = item[2]
break
case 't':
if (preItem[0] === 'Q' || preItem[0] === 'q') {
preCX = preX + preItem[3] - preItem[1]
preCY = preY + preItem[4] - preItem[2]
this.quadraticCurveTo(
preX,
preY,
preCX,
preCY,
preX + item[1],
preY + item[2]
)
} else if (preItem[0] === 'T' || preItem[0] === 't') {
this.quadraticCurveTo(
preX,
preY,
preX + preX - preCX,
preY + preY - preCY,
preX + item[1],
preY + item[2]
)
preCX = preX + preX - preCX
preCY = preY + preY - preCY
}
preX += item[1]
preY += item[2]
break
}
}
}
clone() {
return new Path(this.d, {
lineWidth: this.option.lineWidth,
strokeStyle: this.option.strokeStyle,
fillStyle: this.option.fillStyle
})
}
}
export default Path

View File

@ -0,0 +1,27 @@
import Shape from './shape'
class Polygon extends Shape {
constructor(points, option) {
super()
this.option = option || {}
this.points = points
}
draw() {
this.moveTo(this.points[0], this.points[1])
for (let i = 2, len = this.points.length; i < len; i += 2) {
this.lineTo(this.points[i], this.points[i + 1])
}
this.closePath()
// 路径闭合
// if (this.option.strokeStyle) {
// this.strokeStyle = strokeStyle;
// this.lineWidth(this.option.width);
// this.lineJoin('round');
// this.stroke();
// }
}
}
export default Polygon

View File

@ -0,0 +1,21 @@
import Shape from './shape'
class Polyline extends Shape {
constructor(points, option) {
super()
this.option = option || {}
this.points = points
}
draw() {
this.moveTo(this.points[0], this.points[1])
for (let i = 2, len = this.points.length; i < len; i += 2) {
this.lineTo(this.points[i], this.points[i + 1])
}
this.fill()
}
}
export default Polyline

View File

@ -0,0 +1,17 @@
import Shape from './shape'
class Rect extends Shape {
constructor(width, height, option) {
super()
this.width = width
this.height = height
this.option = option || {}
}
draw() {
this.rect(0, 0, this.width, this.height)
}
}
export default Rect

View File

@ -0,0 +1,64 @@
import Shape from './shape'
class RoundedRect extends Shape {
constructor(width, height, r, option) {
super()
this.option = Object.assign(
{
lineWidth: 1,
lt: true,
rt: true,
lb: true,
rb: true
},
option
)
this.r = r || 0
this.width = width
this.height = height
}
draw() {
const width = this.width,
height = this.height,
r = this.r
const ax = r,
ay = 0,
bx = width,
by = 0,
cx = width,
cy = height,
dx = 0,
dy = height,
ex = 0,
ey = 0
this.moveTo(ax, ay)
if (this.option.rt) {
this.arcTo(bx, by, cx, cy, r)
} else {
this.lineTo(bx, by)
}
if (this.option.rb) {
this.arcTo(cx, cy, dx, dy, r)
} else {
this.lineTo(cx, cy)
}
if (this.option.lb) {
this.arcTo(dx, dy, ex, ey, r)
} else {
this.lineTo(dx, dy)
}
if (this.option.lt) {
this.arcTo(ex, ey, ax, ay, r)
} else {
this.lineTo(ex, ey)
}
}
}
export default RoundedRect

View File

@ -0,0 +1,20 @@
import Shape from './shape'
class Sector extends Shape {
constructor(r, from, to, option) {
super()
this.option = option || {}
this.r = r
this.from = from
this.to = to
}
draw() {
this.moveTo(0, 0)
.arc(0, 0, this.r, this.from, this.to)
.closePath()
}
}
export default Sector

View File

@ -0,0 +1,32 @@
import Graphics from '../graphics'
class Shape extends Graphics {
// constructor() {
// super()
// }
draw() {}
render(ctx) {
this.clear()
this.beginPath()
this.draw()
if (this.option.fillStyle && this.option.fillStyle !== 'none') {
this.fillStyle(this.option.fillStyle)
this.fill()
}
if (this.option.strokeStyle) {
this.strokeStyle(this.option.strokeStyle)
if (this.option.lineWidth !== undefined) {
this.lineWidth(parseFloat(this.option.lineWidth))
}
this.stroke()
}
super.render(ctx)
}
}
export default Shape

View File

@ -0,0 +1,154 @@
import DisplayObject from './display-object'
import util from '../../common/util'
import Bitmap from './bitmap'
class Sprite extends DisplayObject {
constructor(option) {
super()
this.option = option
const len = this.option.imgs.length
let count = 0
const firstImg = this.option.imgs[0]
this.imgMap = {}
if (util.isWeapp) {
this.option.imgs.forEach(img => {
util.getImageInWx(img, result => {
this.imgMap[img] = result.img
count++
if (count === len) {
this.img = this.imgMap[firstImg]
this.rect = [0, 0, 0, 0]
}
})
})
} else {
if (typeof firstImg === 'string') {
const len = this.option.imgs.length
let loadedCount = 0
this.option.imgs.forEach(src => {
if (Bitmap.cache[src]) {
this.imgMap[src] = Bitmap.cache[src]
loadedCount++
if (loadedCount === len) {
this.img = this.imgMap[firstImg]
this.rect = [0, 0, 0, 0]
}
} else {
const img = util.isWegame ? wx.createImage() : new window.Image()
img.onload = () => {
this.imgMap[src] = img
loadedCount++
if (loadedCount === len) {
this.img = this.imgMap[firstImg]
this.rect = [0, 0, 0, 0]
}
Bitmap.cache[src] = img
}
img.src = src
}
})
} else if (firstImg instanceof Bitmap) {
this.rect = [0, 0, 0, 0]
this.img = firstImg.img
} else {
this.rect = [0, 0, 0, 0]
this.img = firstImg
}
}
this.x = option.x || 0
this.y = option.y || 0
this.currentFrameIndex = 0
this.animationFrameIndex = 0
this.currentAnimation = option.currentAnimation || null
this.interval = 1e3 / option.framerate
this.paused = false
this.animationEnd = option.animationEnd || function() {}
if (this.currentAnimation) {
if (option.playOnce) {
this.gotoAndPlayOnce(this.currentAnimation)
} else {
this.gotoAndPlay(this.currentAnimation)
}
}
}
play() {
this.paused = false
}
pause() {
this.paused = true
}
reset() {
this.currentFrameIndex = 0
this.animationFrameIndex = 0
}
updateFrame() {
if (!this.paused) {
let opt = this.option
this.dt = Date.now() - this.startTime
let frames = opt.animations[this.currentAnimation].frames
const len = frames.length
const index = Math.floor((this.dt / this.interval) % len)
this.rect = opt.frames[frames[index]]
const rectLen = this.rect.length
rectLen > 4 && (this.originX = this.rect[2] * this.rect[4])
rectLen > 5 && (this.originY = this.rect[3] * this.rect[5])
if (rectLen > 6) {
const img = this.option.imgs[this.rect[6]]
this.img = typeof img === 'string' ? this.imgMap[img] : img
}
if (
index === len - 1 &&
(!this.endTime || Date.now() - this.endTime > this.interval)
) {
this.endTime = Date.now()
this.animationEnd()
if (this._willDestroy) {
this.destroy()
}
}
}
}
gotoAndPlay(animation) {
this.paused = false
this.reset()
this.currentAnimation = animation
this.startTime = Date.now()
}
gotoAndStop(animation) {
this.reset()
this.paused = true
this.currentAnimation = animation
var opt = this.option
var frames = opt.animations[this.currentAnimation].frames
this.rect = opt.frames[frames[this.animationFrameIndex]]
const rect = this.rect
this.width = rect[2]
this.height = rect[3]
const rectLen = rect.length
rectLen > 4 && (this.originX = rect[2] * rect[4])
rectLen > 5 && (this.originY = rect[3] * rect[5])
if (rectLen > 6) {
const img = this.option.imgs[rect[6]]
this.img = typeof img === 'string' ? this.imgMap[img] : img
}
}
gotoAndPlayOnce(animation) {
this.gotoAndPlay(animation)
this._willDestroy = true
}
}
export default Sprite

View File

@ -0,0 +1,330 @@
import wegameCanvas from './wegame-canvas'
import Group from './group.js'
import Renderer from '../render/renderer.js'
import HitRender from '../render/hit-render.js'
import Event from '../base/event.js'
import WeStage from './we-stage'
import option from '../base/stage-propagation-tag'
class Stage extends Group {
constructor(width, height, renderTo) {
super()
const len = arguments.length
this.isWegame = typeof wx !== 'undefined' && wx.createCanvas
this.moveDetectionInterval = 0
if (len === 0) {
// wegame
this.canvas = wegameCanvas
this.disableMoveDetection = true
this.moveDetectionInterval = 500
} else if (len === 4) {
// weapp
return new WeStage(arguments[0], arguments[1], arguments[2], arguments[3])
} else {
if (len === 1) {
this.canvas =
typeof width === 'string' ? document.querySelector(width) : width
} else {
this.renderTo =
typeof renderTo === 'string'
? document.querySelector(renderTo)
: renderTo
if (this.renderTo.tagName === 'CANVAS') {
this.canvas = this.renderTo
this.canvas.width = width
this.canvas.height = height
} else {
this.canvas = document.createElement('canvas')
this.canvas.width = width
this.canvas.height = height
this.renderTo.appendChild(this.canvas)
}
}
// get rect again when trigger onscroll onresize event!?
this._boundingClientRect = this.canvas.getBoundingClientRect()
this.offset = this._getOffset(this.canvas)
}
this.renderer = new Renderer(this.canvas)
if (this.isWegame) {
wx.onTouchStart(evt => this._handleMouseDown(evt))
wx.onTouchMove(evt => this._handleMouseMove(evt))
wx.onTouchEnd(evt => this._handleMouseUp(evt))
} else {
this.canvas.addEventListener('click', evt => this._handleClick(evt))
this.canvas.addEventListener('mousedown', evt =>
this._handleMouseDown(evt)
)
this.canvas.addEventListener('mousemove', evt =>
this._handleMouseMove(evt)
)
this.canvas.addEventListener('mouseup', evt => this._handleMouseUp(evt))
this.canvas.addEventListener('mouseout', evt => this._handleMouseOut(evt))
this.canvas.addEventListener('touchstart', evt =>
this._handleMouseDown(evt)
)
this.canvas.addEventListener('touchmove', evt =>
this._handleMouseMove(evt)
)
this.canvas.addEventListener('touchend', evt => this._handleMouseUp(evt))
this.canvas.addEventListener('dblclick', evt => this._handleDblClick(evt))
// this.addEvent(this.canvas, "mousewheel", this._handleMouseWheel.bind(this));
document.addEventListener('contextmenu', evt =>
this._handleContextmenu(evt)
)
}
this.borderTopWidth = 0
this.borderLeftWidth = 0
this.hitAABB = false
this._hitRender = new HitRender()
this._overObject = null
this._scaleX = 1
this._scaleY = 1
this._mouseDownX = 0
this._mouseDownY = 0
this._mouseUpX = 0
this._mouseUpY = 0
this.willDragObject = null
this.preStageX = null
this.preStageY = null
this.width = this.canvas.width
this.height = this.canvas.height
this.___instanceof = 'Stage'
this._moveDetectionTime = Date.now()
}
_handleContextmenu(evt) {
this._getObjectUnderPoint(evt)
}
_handleDblClick(evt) {
this._getObjectUnderPoint(evt)
}
_handleClick(evt) {
if (
Math.abs(this._mouseDownX - this._mouseUpX) < 20 &&
Math.abs(this._mouseDownY - this._mouseUpY) < 20
) {
this._getObjectUnderPoint(evt)
}
}
_handleMouseDown(evt) {
if (this.isWegame) {
evt.type = 'touchstart'
}
this.offset = this._getOffset(this.canvas)
let obj = this._getObjectUnderPoint(evt)
this.willDragObject = obj
this._mouseDownX = evt.stageX
this._mouseDownY = evt.stageY
this.preStageX = evt.stageX
this.preStageY = evt.stageY
}
scaleEventPoint(x, y) {
this._scaleX = x
this._scaleY = y
}
_handleMouseUp(evt) {
if (this.isWegame) {
evt.type = 'touchend'
}
const obj = this._getObjectUnderPoint(evt)
this._mouseUpX = evt.stageX
this._mouseUpY = evt.stageY
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
this.willDragObject = null
this.preStageX = null
this.preStageY = null
if (
obj &&
evt.type === 'touchend' &&
Math.abs(this._mouseDownX - this._mouseUpX) < 30 &&
Math.abs(this._mouseDownY - this._mouseUpY) < 30
) {
mockEvt.type = 'tap'
obj.dispatchEvent(mockEvt)
}
}
_handleMouseOut(evt) {
this._computeStageXY(evt)
this.dispatchEvent({
pureEvent: evt,
type: 'mouseout',
stageX: evt.stageX,
stageY: evt.stageY
})
}
_handleMouseMove(evt) {
if (Date.now() - this._moveDetectionTime < this.moveDetectionInterval) {
return
}
this._moveDetectionTime = Date.now()
if (this.isWegame) {
evt.type = 'touchmove'
}
if (this.disableMoveDetection) return
let obj = this._getObjectUnderPoint(evt)
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
if (this.willDragObject) {
mockEvt.type = 'drag'
mockEvt.dx = mockEvt.stageX - this.preStageX
mockEvt.dy = mockEvt.stageY - this.preStageY
this.preStageX = mockEvt.stageX
this.preStageY = mockEvt.stageY
this.willDragObject.dispatchEvent(mockEvt)
}
if (obj) {
if (this._overObject === null) {
mockEvt.type = 'mouseover'
obj.dispatchEvent(mockEvt)
this._overObject = obj
this._setCursor(obj)
} else {
if (obj.id !== this._overObject.id) {
this._overObject.dispatchEvent({
pureEvent: evt,
type: 'mouseout',
stageX: evt.stageX,
stageY: evt.stageY
})
mockEvt.type = 'mouseover'
obj.dispatchEvent(mockEvt)
this._setCursor(obj)
this._overObject = obj
} else {
mockEvt.type = 'mousemove'
obj.dispatchEvent(mockEvt)
mockEvt.type = 'touchmove'
obj.dispatchEvent(mockEvt)
}
}
} else if (this._overObject) {
mockEvt.type = 'mouseout'
this._overObject.dispatchEvent(mockEvt)
this._overObject = null
this._setCursor({ cursor: 'default' })
}
}
_setCursor(obj) {
if (!this.canvas.style) {
return
}
if (obj.cursor) {
this.canvas.style.cursor = obj.cursor
} else if (obj.parent) {
this._setCursor(obj.parent)
} else {
this._setCursor({ cursor: 'default' })
}
}
_getObjectUnderPoint(evt) {
this._computeStageXY(evt)
if (this.hitAABB) {
return this._hitRender.hitAABB(this, evt)
} else {
return this._hitRender.hitPixel(this, evt)
}
}
_computeStageXY(evt) {
this._boundingClientRect = this.isWegame
? { left: 0, top: 0 }
: this.canvas.getBoundingClientRect()
if (evt.touches || evt.changedTouches) {
const firstTouch = evt.touches[0] || evt.changedTouches[0]
if (firstTouch) {
evt.stageX = (firstTouch.pageX - this.offset[0]) / this._scaleX
evt.stageY = (firstTouch.pageY - this.offset[1]) / this._scaleY
}
} else {
evt.stageX =
(evt.clientX - this._boundingClientRect.left - this.borderLeftWidth) /
this._scaleX
evt.stageY =
(evt.clientY - this._boundingClientRect.top - this.borderTopWidth) /
this._scaleY
}
}
_getOffset(el) {
if (this.isWegame) {
return [0, 0]
}
let _t = 0,
_l = 0
if (
document.documentElement.getBoundingClientRect &&
el.getBoundingClientRect
) {
let box = el.getBoundingClientRect()
_l = box.left
_t = box.top
} else {
while (el.offsetParent) {
_t += el.offsetTop
_l += el.offsetLeft
el = el.offsetParent
}
return [_l, _t]
}
return [
_l +
Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
_t + Math.max(document.documentElement.scrollTop, document.body.scrollTop)
]
}
update() {
this.renderer.update(this)
}
on(type, fn) {
this.canvas.addEventListener(type, evt => {
if (!option.stagePropagationStopped[type]) {
this._computeStageXY(evt)
fn(evt)
}
option.stagePropagationStopped[type] = false
})
}
off(type, fn) {
this.canvas.removeEventListener(type, fn)
}
}
export default Stage

View File

@ -0,0 +1,38 @@
import DisplayObject from './display-object'
import util from '../../common/util'
let measureCtx
if (util.isWeapp) {
measureCtx = wx.createCanvasContext('measure0')
} else if (typeof document !== 'undefined') {
measureCtx = document.createElement('canvas').getContext('2d')
}
class Text extends DisplayObject {
constructor(text, option) {
super()
this.text = text
option = option || {}
this.font = option.font || '10px sans-serif'
this.color = option.color || 'black'
this.textAlign = option.textAlign || 'left'
this.baseline = option.baseline || 'top'
}
getWidth() {
if (!measureCtx) {
if (util.isWegame) {
measureCtx = wx.createCanvas().getContext('2d')
}
}
if (this.font) {
measureCtx.font = this.font
}
return measureCtx.measureText(this.text).width
}
}
export default Text

View File

@ -0,0 +1,152 @@
import Group from './group.js'
import Renderer from '../render/renderer.js'
import WxHitRender from '../render/wx-hit-render.js'
import Event from '../base/event.js'
class WeStage extends Group {
constructor(width, height, id, page) {
super()
const component = page.selectComponent('#' + id)
component.setData({
width,
height
})
component.stage = this
const canvasId = component.data.id
const ctx = wx.createCanvasContext(canvasId, component)
const hitCtx = wx.createCanvasContext(canvasId + 'Hit', component)
this.renderer = new Renderer(ctx, width, height)
this._hitRender = new WxHitRender(hitCtx, component, canvasId)
this._overObject = null
this.ctx = ctx
this.hitAABB = true
this.width = width
this.height = height
this.___instanceof = 'Stage'
}
touchStartHandler(evt) {
const p1 = evt.changedTouches[0]
evt.stageX = Math.round(p1.x * this.scaleX)
evt.stageY = Math.round(p1.y * this.scaleY)
this._getObjectUnderPoint(evt, obj => {
this.willDragObject = obj
this._mouseDownX = evt.stageX
this._mouseDownY = evt.stageY
this.preStageX = evt.stageX
this.preStageY = evt.stageY
})
}
touchMoveHandler(evt) {
const p1 = evt.changedTouches[0]
evt.stageX = Math.round(p1.x * this.scaleX)
evt.stageY = Math.round(p1.y * this.scaleY)
this._getObjectUnderPoint(evt, obj => {
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
if (this.willDragObject) {
mockEvt.type = 'drag'
mockEvt.dx = mockEvt.stageX - this.preStageX
mockEvt.dy = mockEvt.stageY - this.preStageY
this.preStageX = mockEvt.stageX
this.preStageY = mockEvt.stageY
this.willDragObject.dispatchEvent(mockEvt)
}
if (obj) {
if (this._overObject === null) {
this._overObject = obj
} else {
if (obj.id !== this._overObject.id) {
this._overObject = obj
} else {
mockEvt.type = 'touchmove'
obj.dispatchEvent(mockEvt)
}
}
} else if (this._overObject) {
this._overObject = null
}
})
}
touchEndHandler(evt) {
const p1 = evt.changedTouches[0]
evt.stageX = Math.round(p1.x * this.scaleX)
evt.stageY = Math.round(p1.y * this.scaleY)
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
this._getObjectUnderPoint(evt, obj => {
this._mouseUpX = evt.stageX
this._mouseUpY = evt.stageY
this.willDragObject = null
this.preStageX = null
this.preStageY = null
if (
obj &&
Math.abs(this._mouseDownX - this._mouseUpX) < 30 &&
Math.abs(this._mouseDownY - this._mouseUpY) < 30
) {
mockEvt.type = 'tap'
obj.dispatchEvent(mockEvt)
}
})
}
_handleMouseOut(evt) {
this.dispatchEvent({
pureEvent: evt,
type: 'mouseout',
stageX: evt.stageX,
stageY: evt.stageY
})
}
_getObjectUnderPoint(evt, cb) {
//const list = this.renderer.getHitRenderList(this)
if (this.hitAABB) {
return this._hitRender.hitAABB(this, evt, cb)
} else {
this._hitRender.clear()
this._hitRender.hit(list, evt, cb, list.length - 1)
}
}
on(type, cb) {
switch (type) {
case 'touchstart':
this.touchStart = cb
break
case 'touchmove':
this.touchMove = cb
break
case 'touchend':
this.touchEnd = cb
break
}
}
update() {
this.renderer.update(this)
}
}
export default WeStage

View File

@ -0,0 +1,11 @@
let wegameCanvas = null
if (typeof wx !== 'undefined') {
// 在开放数据域的环境下,用`wx.getSharedCanvas`创建canvas
if (wx.getSharedCanvas) {
wegameCanvas = wx.getSharedCanvas()
} else if (wx.createCanvas) {
wegameCanvas = wx.createCanvas()
}
}
export default wegameCanvas

View File

@ -0,0 +1,29 @@
## Interface design reference
* https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter
* http://www.runoob.com/cssref/css3-pr-filter.html
* https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function
## Usage
``` js
const bitmap = new cax.Bitmap(img)
bitmap.filter('brightness(0.5)')
//or filter rect of bitmap
//bitmap.filter('brightness(0.5)'), { x: 0, y: 0, width: 80, height: 80 }
```
## Todo
horizontalFlip 和 verticalFlip 来制作镜像 spritesheet
``` js
new cax.Sprite({
imgs:[bitmap.flipX()]
})
//bitmap.flipX()
//flipX return的就是 this.cacheCanvas
//bitmap.filpY()
```

View File

@ -0,0 +1,119 @@
import { createImageData } from './create-image-data'
export function blur(pixels, diameter) {
diameter = Math.abs(diameter)
if (diameter <= 1) return pixels
var radius = diameter / 2
var len = Math.ceil(diameter) + (1 - (Math.ceil(diameter) % 2))
var weights = new Float32Array(len)
var rho = (radius + 0.5) / 3
var rhoSq = rho * rho
var gaussianFactor = 1 / Math.sqrt(2 * Math.PI * rhoSq)
var rhoFactor = -1 / (2 * rho * rho)
var wsum = 0
var middle = Math.floor(len / 2)
for (var i = 0; i < len; i++) {
var x = i - middle
var gx = gaussianFactor * Math.exp(x * x * rhoFactor)
weights[i] = gx
wsum += gx
}
for (var i = 0; i < weights.length; i++) {
weights[i] /= wsum
}
return separableConvolve(pixels, weights, weights, false)
}
function separableConvolve(pixels, horizWeights, vertWeights, opaque) {
return horizontalConvolve(
verticalConvolve(pixels, vertWeights, opaque),
horizWeights,
opaque
)
}
function horizontalConvolve(pixels, weightsVector, opaque) {
var side = weightsVector.length
var halfSide = Math.floor(side / 2)
var src = pixels.data
var sw = pixels.width
var sh = pixels.height
var w = sw
var h = sh
var output = createImageData(w, h)
var dst = output.data
var alphaFac = opaque ? 1 : 0
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var sy = y
var sx = x
var dstOff = (y * w + x) * 4
var r = 0,
g = 0,
b = 0,
a = 0
for (var cx = 0; cx < side; cx++) {
var scy = sy
var scx = Math.min(sw - 1, Math.max(0, sx + cx - halfSide))
var srcOff = (scy * sw + scx) * 4
var wt = weightsVector[cx]
r += src[srcOff] * wt
g += src[srcOff + 1] * wt
b += src[srcOff + 2] * wt
a += src[srcOff + 3] * wt
}
dst[dstOff] = r
dst[dstOff + 1] = g
dst[dstOff + 2] = b
dst[dstOff + 3] = a + alphaFac * (255 - a)
}
}
return output
}
function verticalConvolve(pixels, weightsVector, opaque) {
var side = weightsVector.length
var halfSide = Math.floor(side / 2)
var src = pixels.data
var sw = pixels.width
var sh = pixels.height
var w = sw
var h = sh
var output = createImageData(w, h)
var dst = output.data
var alphaFac = opaque ? 1 : 0
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
var sy = y
var sx = x
var dstOff = (y * w + x) * 4
var r = 0,
g = 0,
b = 0,
a = 0
for (var cy = 0; cy < side; cy++) {
var scy = Math.min(sh - 1, Math.max(0, sy + cy - halfSide))
var scx = sx
var srcOff = (scy * sw + scx) * 4
var wt = weightsVector[cy]
r += src[srcOff] * wt
g += src[srcOff + 1] * wt
b += src[srcOff + 2] * wt
a += src[srcOff + 3] * wt
}
dst[dstOff] = r
dst[dstOff + 1] = g
dst[dstOff + 2] = b
dst[dstOff + 3] = a + alphaFac * (255 - a)
}
}
return output
}

View File

@ -0,0 +1,10 @@
export function brightness(pixels, adjustment) {
const data = pixels.data
const length = data.length
for (let i = 0; i < length; i += 4) {
data[i] += adjustment
data[i + 1] += adjustment
data[i + 2] += adjustment
}
return pixels
}

View File

@ -0,0 +1,19 @@
export function colorize(pixels, option) {
const data = pixels.data
const length = data.length
const hex =
option.color.charAt(0) === '#' ? option.color.substr(1) : option.color
const colorRGB = {
r: parseInt(hex.substr(0, 2), 16),
g: parseInt(hex.substr(2, 2), 16),
b: parseInt(hex.substr(4, 2), 16)
}
for (let i = 0; i < length; i += 4) {
data[i] -= (data[i] - colorRGB.r) * option.amount
data[i + 1] -= (data[i + 1] - colorRGB.g) * option.amount
data[i + 2] -= (data[i + 2] - colorRGB.b) * option.amount
}
return pixels
}

View File

@ -0,0 +1,13 @@
export function contrast(pixels, contrast) {
const data = pixels.data
const length = data.length
const factor = (259 * (contrast + 255)) / (255 * (259 - contrast))
for (let i = 0; i < length; i += 4) {
data[i] = factor * (data[i] - 128) + 128
data[i + 1] = factor * (data[i + 1] - 128) + 128
data[i + 2] = factor * (data[i + 2] - 128) + 128
}
return pixels
}

View File

@ -0,0 +1,11 @@
let tmpCtx = null
if (typeof document !== 'undefined') {
tmpCtx = document.createElement('canvas').getContext('2d')
} else if (typeof wx !== 'undefined' && wx.createCanvas) {
tmpCtx = wx.createCanvas().getContext('2d')
}
export function createImageData(w, h) {
return tmpCtx.createImageData(w, h)
}

View File

@ -0,0 +1,10 @@
export function gamma(pixels, adjustment) {
const data = pixels.data
const length = data.length
for (let i = 0; i < length; i += 4) {
data[i] = Math.pow(data[i] / 255, adjustment) * 255
data[i + 1] = Math.pow(data[i + 1] / 255, adjustment) * 255
data[i + 2] = Math.pow(data[i + 2] / 255, adjustment) * 255
}
return pixels
}

View File

@ -0,0 +1,17 @@
export function grayscale(pixels, adjustment) {
const data = pixels.data
const length = data.length
for (let i = 0; i < length; i += 4) {
let r = data[i]
let g = data[i + 1]
let b = data[i + 2]
// CIE luminance for the RGB
// The human eye is bad at seeing red and blue, so we de-emphasize them.
let v = 0.2126 * r + 0.7152 * g + 0.0722 * b
data[i] = r + (v - r) * adjustment
data[i + 1] = g + (v - g) * adjustment
data[i + 2] = b + (v - b) * adjustment
}
return pixels
}

View File

@ -0,0 +1,55 @@
import { invert } from './invert'
import { blur } from './blur'
import { brightness } from './brightness'
import { contrast } from './contrast'
import { grayscale } from './grayscale'
import { sepia } from './sepia'
import { threshold } from './threshold'
import { gamma } from './gamma'
import { colorize } from './colorize'
export function filter(pixels, name) {
if (typeof name === 'string') {
let type = name.split('(')[0]
let num = getNumber(name)
switch (type) {
case 'invert':
return invert(pixels, num)
case 'brightness':
return brightness(pixels, -255 + num * 255)
case 'blur':
return blur(pixels, num)
case 'contrast':
return contrast(pixels, -255 + num * 255)
case 'grayscale':
return grayscale(pixels, num)
case 'sepia':
return sepia(pixels, num)
case 'threshold':
return threshold(pixels, num)
case 'gamma':
return gamma(pixels, num)
}
} else {
switch (name.type) {
case 'colorize':
return colorize(pixels, name)
}
}
}
function getNumber(str) {
str = str
.replace(
/(invert)|(brightness)|(blur)|(contrast)|(grayscale)|(sepia)|(threshold)|(gamma)?\(/g,
''
)
.replace(')', '')
if (str.indexOf('%') !== -1) {
return Number(str.replace('%', '')) / 100
} else if (str.indexOf('px') !== -1) {
return Number(str.replace('px', ''))
} else {
return Number(str)
}
}

View File

@ -0,0 +1,10 @@
export function invert(pixels, ratio) {
const d = pixels.data
ratio = ratio === undefined ? 1 : ratio
for (var i = 0; i < d.length; i += 4) {
d[i] = d[i] + ratio * (255 - 2 * d[i])
d[i + 1] = d[i + 1] + ratio * (255 - 2 * d[i + 1])
d[i + 2] = d[i + 2] + ratio * (255 - 2 * d[i + 2])
}
return pixels
}

View File

@ -0,0 +1,19 @@
export function sepia(pixels, adjustment) {
const data = pixels.data
const length = data.length
for (let i = 0; i < length; i += 4) {
const r = data[i]
const g = data[i + 1]
const b = data[i + 2]
const sr = r * 0.393 + g * 0.769 + b * 0.189
const sg = r * 0.349 + g * 0.686 + b * 0.168
const sb = r * 0.272 + g * 0.534 + b * 0.131
data[i] = r + (sr - r) * adjustment
data[i + 1] = g + (sg - g) * adjustment
data[i + 2] = b + (sb - b) * adjustment
}
return pixels
}

View File

@ -0,0 +1,12 @@
export function threshold(pixels, threshold) {
const data = pixels.data
const length = data.length
for (let i = 0; i < length; i += 4) {
const r = data[i]
const g = data[i + 1]
const b = data[i + 2]
const v = 0.2126 * r + 0.7152 * g + 0.0722 * b >= threshold ? 255 : 0
data[i] = data[i + 1] = data[i + 2] = v
}
return pixels
}

View File

@ -0,0 +1,260 @@
import Group from '../display/group.js'
import Graphics from '../display/graphics.js'
import Render from './render.js'
import Sprite from '../display/sprite.js'
import Bitmap from '../display/bitmap.js'
import Text from '../display/text.js'
import { filter } from '../filter/index.js'
class CanvasRender extends Render {
constructor(canvasOrContext, width, height) {
super()
if (arguments.length === 3) {
this.ctx = canvasOrContext
this.width = width
this.height = height
} else {
this.ctx = canvasOrContext.getContext('2d')
this.width = canvasOrContext.width
this.height = canvasOrContext.height
}
}
clear(ctx, width, height) {
ctx.clearRect(0, 0, width, height)
}
render(ctx, o, cacheData) {
let mtx = o._matrix
if (o.children) {
let list = o.children.slice(0),
l = list.length
for (let i = 0; i < l; i++) {
let child = list[i]
mtx.initialize(1, 0, 0, 1, 0, 0)
mtx.appendTransform(
o.x,
o.y,
o.scaleX,
o.scaleY,
o.rotation,
o.skewX,
o.skewY,
o.originX,
o.originY
)
// if (!this.checkBoundEvent(child)) continue
ctx.save()
this._render(ctx, child, cacheData ? null : mtx, cacheData, true)
ctx.restore()
}
} else {
this._render(ctx, o, cacheData ? null : mtx, cacheData)
}
}
_render(ctx, o, mtx, cacheData, inGroup) {
if (!o.isVisible()) return
if (mtx && !o.fixed) {
o._matrix.initialize(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
} else if (cacheData && !o.fixed) {
o._matrix.initialize(
cacheData.scale,
0,
0,
cacheData.scale,
cacheData.x * -1,
cacheData.y * -1
)
} else {
o._matrix.initialize(1, 0, 0, 1, 0, 0)
}
mtx = o._matrix
// group 进行 cache canvas 内部的子元素需要进行appendTransform
// cache canvas 渲染不叠加自身的 transform因为进入主渲染会进行appendTransform
if (inGroup || !cacheData) {
mtx.appendTransform(
o.x,
o.y,
o.scaleX,
o.scaleY,
o.rotation,
o.skewX,
o.skewY,
o.originX,
o.originY
)
}
const ocg = o.clipGraphics
if (ocg) {
ctx.beginPath()
ocg._matrix.copy(mtx)
ocg._matrix.appendTransform(
ocg.x,
ocg.y,
ocg.scaleX,
ocg.scaleY,
ocg.rotation,
ocg.skewX,
ocg.skewY,
ocg.originX,
ocg.originY
)
ctx.setTransform(
ocg._matrix.a,
ocg._matrix.b,
ocg._matrix.c,
ocg._matrix.d,
ocg._matrix.tx,
ocg._matrix.ty
)
ocg.render(ctx)
ctx.clip(o.clipRuleNonzero ? 'nonzero' : 'evenodd')
}
const oacg = o.absClipGraphics
if (oacg) {
ctx.beginPath()
oacg._matrix.initialize(1, 0, 0, 1, 0, 0)
oacg._matrix.appendTransform(
oacg.x,
oacg.y,
oacg.scaleX,
oacg.scaleY,
oacg.rotation,
oacg.skewX,
oacg.skewY,
oacg.originX,
oacg.originY
)
ctx.setTransform(
oacg._matrix.a,
oacg._matrix.b,
oacg._matrix.c,
oacg._matrix.d,
oacg._matrix.tx,
oacg._matrix.ty
)
oacg.render(ctx)
ctx.clip(o.absClipRuleNonzero ? 'nonzero' : 'evenodd')
}
// if(!cacheData){
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
// }
if (o._readyToCache || o.cacheUpdating) {
this.setComplexProps(ctx, o)
o._readyToCache = false
o.cacheCtx.clearRect(0, 0, o.cacheCanvas.width, o.cacheCanvas.height)
o.cacheCtx.save()
this.render(o.cacheCtx, o, o._cacheData)
o.cacheCtx.restore()
// debug cacheCanvas
// document.body.appendChild(o.cacheCanvas)
if (o._readyToFilter) {
o.cacheCtx.putImageData(
filter(
o.cacheCtx.getImageData(
0,
0,
o.cacheCanvas.width,
o.cacheCanvas.height
),
o._filterName
),
0,
0
)
this._readyToFilter = false
}
ctx.drawImage(o.cacheCanvas, o._cacheData.x, o._cacheData.y)
} else if (o.cacheCanvas && !cacheData) {
this.setComplexProps(ctx, o)
ctx.drawImage(o.cacheCanvas, o._cacheData.x, o._cacheData.y)
} else if (o instanceof Group) {
let list = o.children.slice(0),
l = list.length
for (let i = 0; i < l; i++) {
ctx.save()
this._render(ctx, list[i], mtx)
ctx.restore()
}
} else if (o instanceof Graphics) {
this.setComplexProps(ctx, o)
o.render(ctx)
} else if (o instanceof Sprite && o.rect) {
this.setComplexProps(ctx, o)
o.updateFrame()
let rect = o.rect
ctx.drawImage(
o.img,
rect[0],
rect[1],
rect[2],
rect[3],
0,
0,
rect[2],
rect[3]
)
} else if (o instanceof Bitmap && o.rect) {
this.setComplexProps(ctx, o)
let bRect = o.rect
ctx.drawImage(
o.img,
bRect[0],
bRect[1],
bRect[2],
bRect[3],
0,
0,
bRect[2],
bRect[3]
)
} else if (o instanceof Text) {
this.setComplexProps(ctx, o)
ctx.font = o.font
ctx.fillStyle = o.color
ctx.textAlign = o.textAlign
ctx.textBaseline = o.baseline
ctx.fillText(o.text, 0, 0)
}
}
setComplexProps(ctx, o) {
o.complexCompositeOperation = ctx.globalCompositeOperation = this.getCompositeOperation(
o
)
o.complexAlpha = ctx.globalAlpha = this.getAlpha(o, 1)
o.complexShadow = this.getShadow(o)
if (o.complexShadow) {
ctx.shadowColor = o.complexShadow.color
ctx.shadowOffsetX = o.complexShadow.offsetX
ctx.shadowOffsetY = o.complexShadow.offsetY
ctx.shadowBlur = o.complexShadow.blur
}
}
getCompositeOperation(o) {
if (o.compositeOperation) return o.compositeOperation
if (o.parent) return this.getCompositeOperation(o.parent)
}
getAlpha(o, alpha) {
var result = o.alpha * alpha
if (o.parent) {
return this.getAlpha(o.parent, result)
}
return result
}
getShadow(o) {
if (o.shadow) return o.shadow
if (o.parent) return this.getShadow(o.parent)
}
}
export default CanvasRender

View File

@ -0,0 +1,296 @@
import Group from '../display/group.js'
import Graphics from '../display/graphics.js'
import Render from './render.js'
import Event from '../base/event.js'
import Sprite from '../display/sprite.js'
import Bitmap from '../display/bitmap.js'
import Text from '../display/text.js'
class HitRender extends Render {
constructor() {
super()
if (typeof wx !== 'undefined' && wx.createCanvas) {
this.canvas = wx.createCanvas()
} else {
this.canvas = document.createElement('canvas')
}
this.canvas.width = 1
this.canvas.height = 1
this.ctx = this.canvas.getContext('2d')
// debug event
// this.canvas.width = 441
// this.canvas.height = 441
// this.ctx = this.canvas.getContext('2d')
// document.body.appendChild(this.canvas)
this.disableEvents = ['mouseover', 'mouseout', 'mousemove', 'touchmove']
}
clear() {
this.ctx.clearRect(0, 0, this.width, this.height)
}
hitAABB(o, evt) {
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
let child = list[i]
// if (!this.isbindingEvent(child)) continue;
let path = this._hitAABB(child, evt, [], true)
if (path.length > 0) {
let target = path[path.length - 1]
this._dispatchEvent(target, evt)
return target
}
}
}
_hitAABB(o, evt, path, rootCall) {
if (o.ignoreHit || !o.isVisible()) {
return
}
o.initAABB()
if (o.AABB && this.checkPointInAABB(evt.stageX, evt.stageY, o.AABB)) {
// this._bubbleEvent(o, type, evt);
o.___$push = true
path.push(o)
//return o
}
if (o instanceof Group) {
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
let child = list[i]
this._hitAABB(child, evt, path)
if (child.___$push) {
delete child.___$push
//同级只找一个就好了,所有 break
break
}
//if (target) return target
}
}
if (rootCall) {
return path
}
}
checkPointInAABB(x, y, AABB) {
let minX = AABB[0]
if (x < minX) return false
let minY = AABB[1]
if (y < minY) return false
let maxX = minX + AABB[2]
if (x > maxX) return false
let maxY = minY + AABB[3]
if (y > maxY) return false
return true
}
hitPixel(o, evt) {
const ctx = this.ctx
ctx.clearRect(0, 0, 2, 2)
let mtx = o._hitMatrix
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
let child = list[i]
mtx.initialize(1, 0, 0, 1, 0, 0)
mtx.appendTransform(
o.x - evt.stageX,
o.y - evt.stageY,
o.scaleX,
o.scaleY,
o.rotation,
o.skewX,
o.skewY,
o.originX,
o.originY
)
// if (!this.checkBoundEvent(child)) continue
ctx.save()
let target = this._hitPixel(child, evt, mtx)
ctx.restore()
if (target) return target
}
}
_hitPixel(o, evt, mtx) {
if (o.ignoreHit || !o.isVisible()) return
let ctx = this.ctx
if (o.fixed) {
o._hitMatrix.initialize(1, 0, 0, 1, -evt.stageX, -evt.stageY)
} else if (mtx) {
o._hitMatrix.initialize(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
} else {
o._hitMatrix.initialize(1, 0, 0, 1, 0, 0)
}
mtx = o._hitMatrix
mtx.appendTransform(
o.x,
o.y,
o.scaleX,
o.scaleY,
o.rotation,
o.skewX,
o.skewY,
o.originX,
o.originY
)
const ocg = o.clipGraphics
if (ocg) {
ctx.beginPath()
ocg._matrix.copy(mtx)
ocg._matrix.appendTransform(
ocg.x,
ocg.y,
ocg.scaleX,
ocg.scaleY,
ocg.rotation,
ocg.skewX,
ocg.skewY,
ocg.originX,
ocg.originY
)
ctx.setTransform(
ocg._matrix.a,
ocg._matrix.b,
ocg._matrix.c,
ocg._matrix.d,
ocg._matrix.tx,
ocg._matrix.ty
)
ocg.render(ctx)
ctx.clip(o.clipRuleNonzero ? 'nonzero' : 'evenodd')
}
const oacg = o.absClipGraphics
if (oacg) {
ctx.beginPath()
oacg._matrix.initialize(1, 0, 0, 1, 0, 0)
oacg._matrix.appendTransform(
oacg.x,
oacg.y,
oacg.scaleX,
oacg.scaleY,
oacg.rotation,
oacg.skewX,
oacg.skewY,
oacg.originX,
oacg.originY
)
ctx.setTransform(
oacg._matrix.a,
oacg._matrix.b,
oacg._matrix.c,
oacg._matrix.d,
oacg._matrix.tx,
oacg._matrix.ty
)
oacg.render(ctx)
ctx.clip(o.absClipRuleNonzero ? 'nonzero' : 'evenodd')
}
if (o.cacheCanvas) {
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
ctx.drawImage(o.cacheCanvas, o._cacheData.x, o._cacheData.y)
} else if (o instanceof Group) {
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
ctx.save()
let target = this._hitPixel(list[i], evt, mtx)
ctx.restore()
if (target) return target
}
} else {
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
if (o instanceof Graphics) {
this.setComplexProps(ctx, o)
o.render(ctx)
} else if (o instanceof Sprite && o.rect) {
this.setComplexProps(ctx, o)
o.updateFrame()
let rect = o.rect
ctx.drawImage(
o.img,
rect[0],
rect[1],
rect[2],
rect[3],
0,
0,
rect[2],
rect[3]
)
} else if (o instanceof Bitmap && o.rect) {
this.setComplexProps(ctx, o)
let bRect = o.rect
ctx.drawImage(
o.img,
bRect[0],
bRect[1],
bRect[2],
bRect[3],
0,
0,
bRect[2],
bRect[3]
)
} else if (o instanceof Text) {
this.setComplexProps(ctx, o)
ctx.font = o.font
ctx.fillStyle = o.color
ctx.textAlign = o.textAlign
ctx.textBaseline = o.baseline
ctx.fillText(o.text, 0, 0)
}
}
if (o.hitBox) {
o.initAABB()
if (this.checkPointInAABB(evt.stageX, evt.stageY, o.AABB)) {
this._dispatchEvent(o, evt)
return o
}
} else if (ctx.getImageData(0, 0, 1, 1).data[3] > 0) {
this._dispatchEvent(o, evt)
return o
}
}
setComplexProps(ctx, o) {
ctx.globalCompositeOperation = o.complexCompositeOperation
ctx.globalAlpha = o.complexAlpha
// The shadow does not trigger the event, so remove it
// if(o.complexShadow){
// ctx.shadowColor = o.complexShadow.color
// ctx.shadowOffsetX = o.complexShadow.offsetX
// ctx.shadowOffsetY = o.complexShadow.offsetY
// ctx.shadowBlur = o.complexShadow.blur
// }
}
_dispatchEvent(obj, evt) {
if (this.disableEvents.indexOf(evt.type) !== -1) return
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
mockEvt.type = evt.type
obj.dispatchEvent(mockEvt)
}
}
export default HitRender

View File

@ -0,0 +1,9 @@
class Render {
render() {}
renderGraphics() {}
clear() {}
}
export default Render

View File

@ -0,0 +1,116 @@
import CanvasRender from '../render/canvas-render'
import Group from '../display/group.js'
class Renderer {
constructor(canvasOrContext, width, height) {
this.renderList = []
if (arguments.length === 3) {
this.renderer = new CanvasRender(canvasOrContext, width, height)
this.width = width
this.height = height
} else {
this.renderer = new CanvasRender(canvasOrContext)
this.width = canvasOrContext.width
this.height = canvasOrContext.height
}
this.ctx = this.renderer.ctx
}
update(stage) {
this.renderer.clear(this.ctx, this.width, this.height)
this.renderer.render(this.ctx, stage)
this.ctx.draw && this.ctx.draw()
}
getHitRenderList(stage) {
const objs = this.renderList
objs.length = 0
this.computeMatrix(stage)
return objs
}
computeMatrix(stage) {
for (var i = 0, len = stage.children.length; i < len; i++) {
this._computeMatrix(stage.children[i])
}
}
initComplex(o) {
o.complexCompositeOperation = this._getCompositeOperation(o)
o.complexAlpha = this._getAlpha(o, 1)
}
_computeMatrix(o, mtx) {
if (!o.isVisible()) {
return
}
if (mtx && !o.fixed) {
o._matrix.initialize(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
} else {
o._matrix.initialize(1, 0, 0, 1, 0, 0)
}
o._matrix.appendTransform(
o.x,
o.y,
o.scaleX,
o.scaleY,
o.rotation,
o.skewX,
o.skewY,
o.originX,
o.originY
)
if (o instanceof Group) {
var list = o.children,
len = list.length,
i = 0
for (; i < len; i++) {
this._computeMatrix(list[i], o._matrix)
}
} else {
// if (o instanceof Graphics) {
// this.renderList.push(o)
// this.initComplex(o)
// } else {
o.initAABB()
// if (this.isInStage(o)) {
this.renderList.push(o)
this.initComplex(o)
// }
// }
}
}
_getCompositeOperation(o) {
if (o.compositeOperation) return o.compositeOperation
if (o.parent) return this._getCompositeOperation(o.parent)
}
_getAlpha(o, alpha) {
var result = o.alpha * alpha
if (o.parent) {
return this._getAlpha(o.parent, result)
}
return result
}
isInStage(o) {
return this.collisionBetweenAABB(o.AABB, this.stage.AABB)
}
collisionBetweenAABB(AABB1, AABB2) {
var maxX = AABB1[0] + AABB1[2]
if (maxX < AABB2[0]) return false
var minX = AABB1[0]
if (minX > AABB2[0] + AABB2[2]) return false
var maxY = AABB1[1] + AABB1[3]
if (maxY < AABB2[1]) return false
var minY = AABB1[1]
if (minY > AABB2[1] + AABB2[3]) return false
return true
}
}
export default Renderer

View File

@ -0,0 +1,190 @@
import Graphics from '../display/graphics.js'
import Render from './render.js'
import Event from '../base/event.js'
import Sprite from '../display/sprite.js'
import Bitmap from '../display/bitmap.js'
import Text from '../display/text.js'
import Group from '../display/group'
class WxHitRender extends Render {
constructor(ctx, component, canvasId) {
super()
this.ctx = ctx
this._isWeapp = true
this._component = component
this._hitCanvasId = canvasId + 'Hit'
this.disableEvents = ['mouseover', 'mouseout', 'mousemove', 'touchmove']
}
clear() {
this.ctx.clearRect(0, 0, 2, 2)
}
// hitAABB (list, evt, cb) {
// const len = list.length
// for (let i = len - 1; i >= 0; i--) {
// let o = list[i]
// if (o.AABB && this.checkPointInAABB(evt.stageX, evt.stageY, o.AABB)) {
// this._dispatchEvent(o, evt)
// cb(o)
// return o
// }
// }
// }
hitAABB(o, evt) {
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
let child = list[i]
// if (!this.isbindingEvent(child)) continue;
let path = this._hitAABB(child, evt, [], true)
if (path.length > 0) {
let target = path[path.length - 1]
this._dispatchEvent(target, evt)
return target
}
}
}
_hitAABB(o, evt, path, rootCall) {
if (o.ignoreHit || !o.isVisible()) {
return
}
o.initAABB()
if (o.AABB && this.checkPointInAABB(evt.stageX, evt.stageY, o.AABB)) {
// this._bubbleEvent(o, type, evt);
o.___$push = true
path.push(o)
//return o
}
if (o instanceof Group) {
let list = o.children.slice(0),
l = list.length
for (let i = l - 1; i >= 0; i--) {
let child = list[i]
this._hitAABB(child, evt, path)
if (child.___$push) {
delete child.___$push
//同级只找一个就好了,所有 break
break
}
//if (target) return target
}
}
if (rootCall) {
return path
}
}
checkPointInAABB(x, y, AABB) {
let minX = AABB[0]
if (x < minX) return false
let minY = AABB[1]
if (y < minY) return false
let maxX = minX + AABB[2]
if (x > maxX) return false
let maxY = minY + AABB[3]
if (y > maxY) return false
return true
}
hit(list, evt, cb, current) {
const ctx = this.ctx
const obj = list[current]
const mtx = obj._hitMatrix.initialize(1, 0, 0, 1, 0, 0)
ctx.save()
mtx.appendTransform(
obj.x - evt.stageX,
obj.y - evt.stageY,
obj.scaleX,
obj.scaleY,
obj.rotation,
obj.skewX,
obj.skewY,
obj.originX,
obj.originY
)
ctx.globalCompositeOperation = obj.complexCompositeOperation
ctx.globalAlpha = obj.complexAlpha
ctx.setTransform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty)
if (obj instanceof Graphics) {
obj.render(ctx)
} else if (obj instanceof Sprite && obj.rect) {
obj.updateFrame()
const rect = obj.rect
ctx.drawImage(
obj.img,
rect[0],
rect[1],
rect[2],
rect[3],
0,
0,
rect[2],
rect[3]
)
} else if (obj instanceof Bitmap && obj.rect) {
const bRect = obj.rect
ctx.drawImage(
obj.img,
bRect[0],
bRect[1],
bRect[2],
bRect[3],
0,
0,
bRect[2],
bRect[3]
)
} else if (obj instanceof Text) {
ctx.font = obj.font
ctx.fillStyle = obj.color
ctx.textAlign = obj.textAlign
ctx.fillText(obj.text, 0, 0)
}
ctx.restore()
current--
ctx.draw(false, () => {
wx.canvasGetImageData(
{
canvasId: this._hitCanvasId,
x: 0,
y: 0,
width: 1,
height: 1,
success: res => {
if (res.data[3] > 1) {
this._dispatchEvent(obj, evt)
cb(obj)
} else {
if (current > -1) {
this.hit(list, evt, cb, current)
}
}
}
},
this._component
)
})
}
_dispatchEvent(obj, evt) {
if (this.disableEvents.indexOf(evt.type) !== -1) return
let mockEvt = new Event()
mockEvt.stageX = evt.stageX
mockEvt.stageY = evt.stageY
mockEvt.pureEvent = evt
mockEvt.type = evt.type
obj.dispatchEvent(mockEvt)
}
}
export default WxHitRender

28
packages/mps/cax/svg.js Normal file
View File

@ -0,0 +1,28 @@
import cax, { html, SVG } from './cax'
function renderSVG(vdom, canvas, scope) {
const w = vdom.props ? vdom.props.width : 300
const h = vdom.props ? vdom.props.height : 150
const stage = new cax.Stage(
w,
h,
canvas,
scope
)
const svg = new SVG(vdom)
stage.add(svg)
stage.update()
triggerAddedStage(svg)
return svg.children[0]
}
function triggerAddedStage(svg) {
svg.addedStage && svg.addedStage()
svg.children &&
svg.children.forEach(child => {
triggerAddedStage(child)
})
}
export { renderSVG, html }

View File

@ -0,0 +1,47 @@
import pathTransition from '../pasition/index'
import { toSVGString } from '../common/util'
export function animate(obj, option) {
const valueList = option.values.split(';').map(item=>{return item.replace(/\\\w/g, '').trim()}).filter(item => item !== '')
let index = 0,
count = 0,
stage
const len = valueList.length
const duration = (parseFloat(option.dur) / len) * 1000
function _animate() {
const nextIndex = index + 1 === len ? 0 : index + 1
if (index + 1 === len) count++
pathTransition.animate({
from: valueList[index],
to: valueList[nextIndex],
duration: duration,
easing: function(v) {
return v
},
begin: function() {
stage = obj.stage
},
progress: function(shapes, percent) {
obj.d = toSVGString(shapes)
stage.update()
},
end: function(shapes) {
index = nextIndex
if (option.repeatCount === 'indefinite') {
_animate()
} else if (count < Number(option.repeatCount)) {
_animate()
}
}
})
}
if (obj.stage) {
_animate()
} else {
obj.addedStage = () => {
_animate()
}
}
}

View File

@ -0,0 +1,23 @@
import Circle from '../render/display/shape/circle'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function circle(props) {
const options = Object.assign(
{
r: 0,
cx: 0,
cy: 0
},
props
)
const circle = new Circle(Number(options.r), parseStyle(props))
// circle.x = Number(options.cx)
// circle.y = Number(options.cy)
transform(props, circle, Number(options.cx), Number(options.cy))
parseEvent(props, circle)
return circle
}

View File

@ -0,0 +1,27 @@
import Ellipse from '../render/display/shape/ellipse'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function ellipse(props) {
const options = Object.assign(
{
rx: 0,
ry: 0,
cx: 0,
cy: 0
},
props
)
const ellipse = new Ellipse(
Number(options.rx) * 2,
Number(options.ry) * 2,
parseStyle(props)
)
// ellipse.x = Number(options.cx)
// ellipse.y = Number(options.cy)
transform(props, ellipse, Number(options.cx), Number(options.cy))
parseEvent(props, ellipse)
return ellipse
}

View File

@ -0,0 +1,24 @@
import Group from '../render/display/group'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function group(props) {
const options = Object.assign(
{
width: 0,
height: 0,
x: 0,
y: 0
},
props
)
const obj = new Group()
// obj.x = Number(options.x)
// obj.y = Number(options.y)
transform(props, obj, Number(options.x), Number(options.y))
parseEvent(props, obj)
return obj
}

View File

@ -0,0 +1,92 @@
const htm = (function() {
var n = function(e, t, r, u) {
for (var o = 1; o < t.length; o++) {
var f = t[o++],
p = 'number' == typeof f ? r[f] : f
1 === t[o]
? (u[0] = p)
: 2 === t[o]
? ((u[1] = u[1] || {})[t[++o]] = p)
: 3 === t[o]
? (u[1] = Object.assign(u[1] || {}, p))
: u.push(t[o] ? e.apply(null, n(e, p, r, ['', null])) : p)
}
return u
},
e = function(n) {
for (
var e,
t,
r = 1,
u = '',
o = '',
f = [0],
p = function(n) {
1 === r && (n || (u = u.replace(/^\s*\n\s*|\s*\n\s*$/g, '')))
? f.push(n || u, 0)
: 3 === r && (n || u)
? (f.push(n || u, 1), (r = 2))
: 2 === r && '...' === u && n
? f.push(n, 3)
: 2 === r && u && !n
? f.push(!0, 2, u)
: 4 === r && t && (f.push(n || u, 2, t), (t = '')),
(u = '')
},
s = 0;
s < n.length;
s++
) {
s && (1 === r && p(), p(s))
for (var i = 0; i < n[s].length; i++)
(e = n[s][i]),
1 === r
? '<' === e
? (p(), (f = [f]), (r = 3))
: (u += e)
: o
? e === o
? (o = '')
: (u += e)
: '"' === e || "'" === e
? (o = e)
: '>' === e
? (p(), (r = 1))
: r &&
('=' === e
? ((r = 4), (t = u), (u = ''))
: '/' === e
? (p(),
3 === r && (f = f[0]),
(r = f),
(f = f[0]).push(r, 4),
(r = 0))
: ' ' === e || '\t' === e || '\n' === e || '\r' === e
? (p(), (r = 2))
: (u += e))
}
return p(), f
},
t = 'function' == typeof Map,
r = t ? new Map() : {},
u = t
? function(n) {
var t = r.get(n)
return t || r.set(n, (t = e(n))), t
}
: function(n) {
for (var t = '', u = 0; u < n.length; u++)
t += n[u].length + '-' + n[u]
return r[t] || (r[t] = e(n))
}
return function(e) {
var t = n(this, u(e), arguments, [])
return t.length > 1 ? t : t[0]
}
})()
function h(type, props, ...children) {
return { type, props, children }
}
export default htm.bind(h)

View File

@ -0,0 +1,94 @@
import Group from '../render/display/group'
import { rect } from './rect'
import { circle } from './circle'
import { ellipse } from './ellipse'
import { line } from './line'
import { polyline } from './polyline'
import { polygon } from './polygon'
import { path } from './path'
import { pasition } from './pasition'
import { group } from './group'
import { animate } from './animate'
class SVG extends Group {
constructor(vdom) {
super()
this.vdom = vdom
if (Object.prototype.toString.call(this.vdom) === '[object Array]') {
this.vdom = this.vdom.filter(item => typeof item !== 'string')[0]
}
const root = new Group()
const options = Object.assign(
{
x: 0,
y: 0
},
vdom.props
)
this.vdom.children && this.vdom.children.forEach(vdomChild => {
this.generate(root, vdomChild)
})
root.x = Number(options.x)
root.y = Number(options.y)
this.add(root)
}
generate(parent, vdomChild) {
switch (vdomChild.type) {
case 'rect':
parent.add(rect(vdomChild.props))
break
case 'circle':
parent.add(circle(vdomChild.props))
break
case 'ellipse':
parent.add(ellipse(vdomChild.props))
break
case 'line':
parent.add(line(vdomChild.props))
break
case 'polyline':
parent.add(polyline(vdomChild.props))
break
case 'polygon':
parent.add(polygon(vdomChild.props))
break
case 'path':
const obj = path(vdomChild.props)
parent.add(obj)
if (
vdomChild.children &&
vdomChild.children[0] &&
vdomChild.children[0].type === 'animate'
) {
animate(obj, vdomChild.children[0].props)
}
break
case 'pasition':
parent.add(pasition(vdomChild.props))
break
case 'g':
const p = group(vdomChild.props)
parent.add(p)
vdomChild.children.forEach(child => {
this.generate(p, child)
})
break
}
}
}
export default SVG

View File

@ -0,0 +1,17 @@
import Line from '../render/display/shape/line'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function line(props) {
const obj = new Line(
Number(props.x1),
Number(props.y1),
Number(props.x2),
Number(props.y2),
parseStyle(props)
)
transform(props, obj)
parseEvent(props, obj)
return obj
}

View File

@ -0,0 +1,32 @@
export function parseEvent(props, obj) {
if (!props) return
const tapHandler =
props.bindtap ||
props.bindTap ||
props.onTap ||
props.ontap ||
props.onclick ||
props.onClick ||
props.bindclick ||
props.bindClick
if (tapHandler) {
let _x = null,
_y = null
obj.on('touchstart', evt => {
_x = evt.stageX
_y = evt.stageY
})
obj.on('touchend', evt => {
if (_x !== null) {
if (Math.abs(evt.stageX - _x) < 20 && Math.abs(evt.stageY - _y) < 20) {
tapHandler(evt)
_x = null
_y = null
}
}
})
}
}

View File

@ -0,0 +1,42 @@
//https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
const map = {
stroke: 'strokeStyle',
fill: 'fillStyle',
'stroke-width': 'lineWidth'
}
const attrs = ['stroke', 'fill', 'stroke-width']
export function parseStyle(props) {
let style = {}
const cssText = props.style
if (props['stroke-width']) {
style.lineWidth = props['stroke-width']
}
if (props['stroke']) {
style.strokeStyle = props['stroke']
}
if (props['fill']) {
style.fillStyle = props['fill']
}
if (!cssText) return style
let cssTxt = cssText.replace(/\/\*(.|\s)*?\*\//g, ' ').replace(/\s+/g, ' ')
let [a, b, rule] = cssTxt.match(/ ?(.*?) ?{([^}]*)}/) || [a, b, cssTxt]
//let cssToJs = s => s.replace(/\W+\w/g, match => match.slice(-1).toUpperCase())
let properties = rule
.split(';')
.map(o => o.split(':').map(x => x && x.trim()))
for (let [property, value] of properties) {
property && (style[map[property]] = value)
}
attrs.forEach(attr => {
if (props.hasOwnProperty(attr)) {
style[map[attr]] = props[attr]
}
})
return style
}

View File

@ -0,0 +1,85 @@
import mt from '../render/base/matrix-transform'
import Matrix2D from '../render/base/matrix2d'
function parse(a) {
a = a.replace(/,/g, ' ').replace(/\s+/g, ',')
const data = {}
const order = []
for (let i in (a = a.match(/(\w+\((\-?\d+\.?\d*e?\-?\d*,?)+\))+/g))) {
let c = a[i].match(/[\w\.\-]+/g)
const key = c.shift()
c = c.map(item => Number(item))
order.push(key)
data[key] = c
}
return {
order: order,
data: data
}
}
export function transform(props, target, x, y) {
if (!props) return
const args = []
if (arguments.length > 2) {
args.push(mt['translate'].apply(null, [x, y]))
target.originX = x * -1
target.originY = y * -1
}
if (props.transform) {
const obj = parse(props.transform)
if (obj.data.matrix) {
args.push({ a: obj.data.matrix[0], b: obj.data.matrix[1], c: obj.data.matrix[2], d: obj.data.matrix[3], e: obj.data.matrix[4], f: obj.data.matrix[5] })
} else {
obj.order.forEach(prop => {
if (prop === 'rotate') {
obj.data[prop][0] *= Math.PI / 180
//svg rotate 2、3个参数不影响 positionorigin会影响position所以注释
//if (obj.data[prop].length > 1) {
// target.originX = obj.data[prop][1] * -1
// target.originY = obj.data[prop][2] * -1
//}
}
if (prop === 'skewX') {
args.push(mt['skew'].apply(null, [obj.data[prop][0] * Math.PI / -180, 0]))
} else if (prop === 'skewY') {
args.push(mt['skew'].apply(null, [0, obj.data[prop][0] * Math.PI / 180]))
} else if (prop === 'skew') {
args.push(mt['skew'].apply(null, [obj.data[prop][0] * Math.PI / -180, obj.data[prop][1] * Math.PI / 180]))
} else {
args.push(mt[prop].apply(null, obj.data[prop]))
}
})
}
}
const mts = args.length > 0 ? mt.compose.apply(null, args) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }
const t = {}
Matrix2D.decompose(mts.a, mts.b, mts.c, mts.d, mts.e, mts.f, t)
target.rotation = t.rotation * 57.29577951308232
target.x = parseFloat(t.x) + target.originX
target.y = parseFloat(t.y) + target.originY
target.scaleY = t.scaleY
target.scaleX = t.scaleX
target.skewX = t.skewX * 57.29577951308232
target.skewY = t.skewY * 57.29577951308232
if (props.width && props.height) {
target.width = parseFloat(props.width)
target.height = parseFloat(props.height)
}
if (props['bounds-x']) {
target.boundsX = parseFloat(props['bounds-x'])
}
if (props['bounds-y']) {
target.boundsY = parseFloat(props['bounds-y'])
}
}

View File

@ -0,0 +1,65 @@
import Path from '../render/display/shape/path'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
import pathTransition from '../pasition/index'
import color from '../common/color'
import { toSVGString } from '../common/util'
export function pasition(props) {
const lerp = color.lerp
const obj = new Path(props.from, parseStyle(props))
const fs = props['from-stroke']
const ts = props['to-stroke']
const ff = props['from-fill']
const tf = props['to-fill']
if (fs) {
obj.option.strokeStyle = fs
}
if (ff) {
obj.option.fillStyle = ff
}
obj.pasitionTo = props.to
obj.pasitionFrom = props.from
transform(props, obj)
parseEvent(props, obj)
let stage,
isFrom = true,
animating = false
obj.toggle = () => {
if (animating) return
pathTransition.animate({
from: isFrom ? props.from : props.to,
to: isFrom ? props.to : props.from,
duration:
typeof props.duration !== 'undefined' ? Number(props.duration) : 600,
easing: function(v) {
return v
},
begin: function() {
stage = obj.stage
animating = true
},
progress: function(shapes, percent) {
obj.d = toSVGString(shapes)
percent *= 100
if (isFrom) {
fs && (obj.option.strokeStyle = lerp(fs, ts, percent))
ff && (obj.option.fillStyle = lerp(ff, tf, percent))
} else {
fs && (obj.option.strokeStyle = lerp(ts, fs, percent))
ff && (obj.option.fillStyle = lerp(tf, ff, percent))
}
stage.update()
//
},
end: function(shapes) {
isFrom = !isFrom
animating = false
}
})
}
return obj
}

View File

@ -0,0 +1,11 @@
import Path from '../render/display/shape/path'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function path(props) {
const obj = new Path(props.d, parseStyle(props))
transform(props, obj)
parseEvent(props, obj)
return obj
}

View File

@ -0,0 +1,15 @@
import Polygon from '../render/display/shape/polygon'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function polygon(props) {
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)
return obj
}

View File

@ -0,0 +1,12 @@
import Polyline from '../render/display/shape/polyline'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function polyline(props) {
const points = props.points.split(/\s+|,/).map(item => Number(item))
const obj = new Polyline(points, parseStyle(props))
transform(props, obj)
parseEvent(props, obj)
return obj
}

View File

@ -0,0 +1,32 @@
import Rect from '../render/display/shape/rect'
import { parseStyle } from './parse-style'
import { transform } from './parse-transform'
import { parseEvent } from './parse-event'
export function rect(props) {
const options = Object.assign(
{
width: 0,
height: 0,
x: 0,
y: 0
},
props
)
const rect = new Rect(
Number(options.width),
Number(options.height),
parseStyle(props)
)
//移入transform里统一计算
//rect.x = Number(options.x)
//rect.y = Number(options.y)
transform(props, rect, Number(options.x), Number(options.y))
parseEvent(props, rect)
return rect
}

View File

@ -16,16 +16,6 @@ class WeElement {
}
}`}
// var code = require("babel-core").transform("<div dd='1'>aaa</div>",{
// "plugins": [
// ["transform-react-jsx", {
// "pragma": "global.__h"
// }]
// ]
// }).code;
// console.log(code)
var baseOptions = {
isRoot: false,
isApp: false,
@ -81,22 +71,22 @@ function compile(file, watch) {
var dir = path.dirname(file.path)
var name = path.basename(file.path, '.jsx')
console.log('[编译文件]'.green , file.path)
console.log('[编译文件]'.green, file.path)
var template = jsx2wxml.default({
...baseOptions,
code: buildComponent(file.contents)
}).template.replace(/<block>/,'').replace(/([\s\S]*)<\/block>/,'$1')
console.log('[编译完成]'.green , file.path)
}).template.replace(/<block>/, '').replace(/([\s\S]*)<\/block>/, '$1')
console.log('[编译完成]'.green, file.path)
const res = prettier.format(template, { parser: "angular" })
console.log('[代码美化]'.green , name + '.wxml' )
console.log('[代码美化]'.green, name + '.wxml')
fs.writeFileSync(dir + '/' + name + '.wxml', res)
console.log('[写入文件]' .green , name + '.wxml')
console.log('[写入文件]'.green, name + '.wxml')
if(watch){
console.log('[编译完成]'.green , name + '.wxml' )
console.log('[监听更改]'.green, '...' )
if (watch) {
console.log('[编译完成]'.green, name + '.wxml')
console.log('[监听更改]'.green, '...')
}
}
@ -106,8 +96,8 @@ function compileLess(file, watch) {
console.log('[编译文件]'.green, file.path)
less.render(file.contents, {
paths: ['.', './common-less'],
}, function (e, output) {
paths: ['.', './common-less'],
}, function (e, output) {
console.log('[编译完成]'.green, file.path)
fs.writeFileSync(dir + '/' + name + '.wxss', output.css)
@ -136,9 +126,60 @@ gulp.task('compileLess', () => {
})
gulp.task('default', ['compile', 'compileLess', 'watch', 'watchLess', 'watchCommonLess'])
console.log('[开始编译]'.green ,'...')
gulp.start('default',function(){
console.log('[编译完成]'.green , '恭喜你全部文件编译完成。' )
console.log('[监听更改]'.green, '...' )
gulp.task('compileSVG', () => {
return gulp
.src(['./**/*.svg', '!./node_modules/**', '!./_scripts/**'])
.pipe(
tap(file => {
compileSVG({
path: file.path,
contents: file.contents.toString()
})
})
)
})
gulp.task('watchSVG', () => {
watch(['./**/*.svg', '!./node_modules/**', '!./_scripts/**'], { events: ['add', 'change'] }, (evt, type) => {
var contents = fs.readFileSync(evt.path)
compileSVG({
path: evt.path,
contents: contents.toString()
}, true)
})
})
function compileSVG(file, watch) {
var dir = path.dirname(file.path)
var name = path.basename(file.path, '.svg')
console.log('[编译文件]'.green, file.path)
var code = require("babel-core").transform(file.contents, {
"plugins": [
["transform-react-jsx", {
"pragma": "h"
}]
]
}).code;
console.log('[编译完成]'.green, file.path)
fs.writeFileSync(dir + '/' + name + '.js', prettier.format(`const h = (type, props, ...children)=>({ type, props, children });export default ${code}`, { parser: "babel" }))
console.log('[写入文件]'.green, name + '.js')
if (watch) {
console.log('[编译完成]'.green, name + '.js')
console.log('[监听更改]'.green, '...')
}
}
gulp.task('default', ['compile', 'compileLess', 'compileSVG', 'watch', 'watchLess', 'watchCommonLess', 'watchSVG'])
console.log('[开始编译]'.green, '...')
gulp.start('default', function () {
console.log('[编译完成]'.green, '恭喜你全部文件编译完成。')
console.log('[监听更改]'.green, '...')
})

View File

@ -1,3 +1,6 @@
import testSVG from '../../svg/test'
import { renderSVG } from '../../cax/svg'
//index.js
//获取应用实例
const app = getApp()
@ -16,6 +19,8 @@ Page({
})
},
onLoad: function () {
renderSVG(testSVG, 'svg-a', this)
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,

View File

@ -1,3 +1,5 @@
{
"usingComponents": {}
"usingComponents": {
"cax-element":"../../cax/index"
}
}

View File

@ -19,4 +19,5 @@
<view class="usermotto">
<text class="user-motto">{motto}</text>
</view>
<cax-element id='svg-a'></cax-element>
</view>

View File

@ -1,7 +1,7 @@
<view class="container">
<view class="userinfo">
<block>
<block wx:if="{{!hasUserInfo && canIUse}}">
<block wx:if="{{ !hasUserInfo && canIUse }}">
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">
获取头像昵称
</button>
@ -11,13 +11,16 @@
<image
bindtap="bindViewTap"
class="userinfo-avatar"
src="{{userInfo.avatarUrl}}"
src="{{ userInfo.avatarUrl }}"
mode="cover"
></image
><text class="userinfo-nickname">{{userInfo.nickName}}</text>
><text class="userinfo-nickname">{{ userInfo.nickName }}</text>
</block>
</block>
</block>
</view>
<view class="usermotto"><text class="user-motto">{{motto}}</text> </view>
<view class="usermotto"
><text class="user-motto">{{ motto }}</text>
</view>
<cax-element id="svg-a"></cax-element>
</view>

View File

@ -1,9 +1,9 @@
<view class="container log-list"
><text
class="log-item"
wx:for="{{logs}}"
wx:for="{{ logs }}"
wx:for-item="log"
wx:for-index="index"
>{{index + 1}}. {{log}}</text
>{{ index + 1 }}. {{ log }}</text
>
</view>

View File

@ -52,6 +52,8 @@
"hidedInDevtools": []
},
"isGameTourist": false,
"simulatorType": "wechat",
"simulatorPluginLibVersion": {},
"condition": {
"search": {
"current": -1,

View File

@ -0,0 +1,7 @@
{
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
"rules": [{
"action": "allow",
"page": "*"
}]
}

View File

@ -1,14 +1,10 @@
export default function () {
return cax.h(
"svg",
{ x: "100", y: "100" },
cax.h("polyline", {
bindtap: this.tapHandler,
points: "30,2 80,2 55,52",
style: "stroke:#006600; stroke-width: 4; fill: #33cc33;"
})
);
}
const h = (type, props, ...children) => ({ type, props, children });
export default h(
"svg",
{ width: "100", height: "100" },
h("polyline", {
bindtap: "tapHandler",
points: "30,2 80,2 55,52",
style: "stroke:#006600; stroke-width: 4; fill: #33cc33;"
})
);

View File

@ -1,4 +1,4 @@
<svg x="100" y="100">
<svg width="100" height="100">
<polyline bindtap="tapHandler" points="30,2 80,2 55,52"
style="stroke:#006600; stroke-width: 4;

Before

Width:  |  Height:  |  Size: 167 B

After

Width:  |  Height:  |  Size: 176 B