Commit 91796154 authored by wjf's avatar wjf

l

parents
Pipeline #160912 failed with stages
in 0 seconds
# 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
# 可以使用标准的 glob 模式匹配。
# 匹配模式最后跟反斜杠(/)说明要忽略的是目录。
# 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(! )取反。
# 所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任
# 意字符; [abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一
# 个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分
# 隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到
# 9 的数字)。
# 书上的一个例子
# #此为注释 – 将被 Git 忽略
# *.a
# 忽略所有 .a 结尾的文件
# !lib.a
# 但 lib.a 除外
# /TODO
# 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
# build/
# 忽略 build/ 目录下的所有文件
# doc/*.txt
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
node_modules
node_modules/*
tests/*
{
"liveServer.settings.port": 5502
}
\ No newline at end of file
# SVGAPlayer-Web
## Install
### Add CreateJS library
```html
<script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
```
### Prebuild JS
1. Goto [https://github.com/yyued/SVGAPlayer-Web/tree/master/build](https://github.com/yyued/SVGAPlayer-Web/tree/master/build) Download svga.createjs.min.js
2. Add ```<script src="svga.createjs.min.js"></script>``` to xxx.html
### NPM
1. ```npm install svgaplayerweb --save```
2. Add ``` require('svgaplayerweb/build/svga.createjs.min') ``` to ```xxx.js```
### SVGA-Format 1.x support
Both Prebuild & NPM, if you need to support SVGA-Format 1.x, add JSZip script to html.
```html
<script src="http://assets.dwstatic.com/common/lib/??jszip/3.1.3/jszip.min.js,jszip/3.1.3/jszip-utils.min.js" charset="utf-8"></script>
```
## Usage
```js
var displayObject = new SVGA.CreatejsPlayer('./samples/rose_2.0.0.svga');
displayObject.onError(function(err) {
console.error(err)
})
displayObject.setFrame(0, 0, 500, 500)
var stage = new createjs.Stage('CanvasID');
stage.addChild(displayObject);
```
### Replace Animation Images Dynamically
You can replace specific image by yourself, ask your designer tell you the ImageKey.
* The Replacing Image MUST have same WIDTH and HEIGHT as Original.
* setImage operation MUST set BEFORE startAnimation.
```
displayObject.setImage('http://yourserver.com/xxx.png', 'ImageKey');
```
### Add Text on Animation Image Dynamically
You can add text on specific image, ask your designer tell you the ImageKey.
* setText operation MUST set BEFORE startAnimation.
```
displayObject.setText('Hello, World!', 'ImageKey');
```
```
displayObject.setText({
text: 'Hello, World!,
size: "24px",
color: "#ffe0a4",
offset: {x: 0.0, y: 0.0}
}, 'ImageKey'); // customize text styles.
```
## Classes
### CreatejsPlayer
You use SVGA.CreatejsPlayer controls animation play and stop.
#### Properties
* int loops; - Animation loop count, defaults to 0 means infinity loop.
* BOOL clearsAfterStop; - defaults to true, means player will clear all contents after stop.
* string fillMode; - defaults to Forward,optional Forward / Backward,fillMode = Forward,Animation will pause on last frame while finished,fillMode = Backward , Animation will pause on first frame.
#### Methods
* constructor (url: string, autoPlay: boolean);
* startAnimation(, reverse: boolean = false); - start animation from zero frame.
* startAnimationWithRange(range: {location: number, length: number}, reverse: boolean = false); - start animation in [location, location+length] frame range.
* pauseAnimation(); - pause animation on current frame.
* stopAnimation(); - stop animation, clear contents while clearsAfterStop === true
* setContentMode(mode: "ScaleToFill" | "AspectFill" | "AspectFit"); - Specific Scale Mode
* setClipsToBounds(clipsToBounds: boolean); - Clips if image render out of box.
* clear(); - force clear contents.
* stepToFrame(frame: int, andPlay: Boolean); - stop to specific frame, play animation while andPlay === true
* stepToPercentage(percentage: float, andPlay: Boolean); - stop to specific percentage, play animation while andPlay === true
* setImage(image: string, forKey: string, transform: [a, b, c, d, tx, ty]); - Replace Animation Images Dynamically, transform is optional, transform could adjust replacing image.
* setText(text: string | {text: string, font: string, size: string, color: string, offset: {x: float, y: float}}, forKey: string); - Add Text on Animation Image Dynamically
* clearDynamicObjects(); - clear all dynamic objects.
#### Callback Method
* onError(callback: (error: Error) => void): void; - call after load failure.
* onFinished(callback: () => void): void; - call after animation stop.
* onFrame(callback: (frame: number): void): void; - call after animation specific frame rendered.
* onPercentage(callback: (percentage: number): void): void; - call after animation specific percentage rendered.
## Issues
### Android 4.x Breaks
As known, some Android OS lack Blob support, add Blob Polyfill by yourself.
```
<script src="//cdn.bootcss.com/blob-polyfill/1.0.20150320/Blob.min.js"></script>
```
## Attention
* Do not ask any usage question, issue board is a issue board, accept library bugs only.
* If you are facing any usage problem, read the README again.
## Issue Template
Issue Description(What's your problem)
How To Reappear(How to reappear the issue)
Any Attachment(Provide a sample about your issue)
------ 中文分割线 ------
## 注意
* 不要在 Issue 板块提问使用问题,Issue 板块只接受 Bug 反馈。
* 如果遇到使用上的问题,仔细阅读 README。
## Issue 模板
请尽量使用英文提交 Issue
请确切回答:问题的描述、重现方式、附件(提供一个 Demo 以重现问题)
This diff is collapsed.
# SVGAPlayer-Web
## Install
### Patch laya.webgl.js library
1. Goto [https://github.com/yyued/SVGAPlayer-Web/tree/master/patch/layabox](https://github.com/yyued/SVGAPlayer-Web/tree/master/patch/layabox) Download laya.webgl.js
2. Replace to {LayaProjectDir}/bin/libs/laya.webgl.js
### Prebuild JS
1. Goto [https://github.com/yyued/SVGAPlayer-Web/tree/master/build](https://github.com/yyued/SVGAPlayer-Web/tree/master/build) Download svga.layabox.min.js
2. Add ```<script src="svga.layabox.min.js"></script>``` to index.html
3. Optional add ```index.d.ts``` to your project.
### SVGA-Format 1.x support
If you need to support SVGA-Format 1.x, add JSZip script to html.
```html
<script src="http://assets.dwstatic.com/common/lib/??jszip/3.1.3/jszip.min.js,jszip/3.1.3/jszip-utils.min.js" charset="utf-8"></script>
```
## Usage
```js
const displayObject = new SVGA.LayaboxPlayer('res/svga/rose_2.0.0.svga')
displayObject.setFrame(0, 0, 750, 750);
Laya.stage.addChild(displayObject as any)
```
### Replace Animation Images Dynamically
You can replace specific image by yourself, ask your designer tell you the ImageKey.
* The Replacing Image MUST have same WIDTH and HEIGHT as Original.
* setImage operation MUST set BEFORE startAnimation.
```
displayObject.setImage('http://yourserver.com/xxx.png', 'ImageKey');
```
### Add Text on Animation Image Dynamically
You can add text on specific image, ask your designer tell you the ImageKey.
* setText operation MUST set BEFORE startAnimation.
```
displayObject.setText('Hello, World!', 'ImageKey');
```
```
displayObject.setText({
text: 'Hello, World!,
size: "24px",
color: "#ffe0a4",
offset: {x: 0.0, y: 0.0}
}, 'ImageKey'); // customize text styles.
```
## Classes
### Player
You use SVGA.Player controls animation play and stop.
#### Properties
* int loops; - Animation loop count, defaults to 0 means infinity loop.
* BOOL clearsAfterStop; - defaults to true, means player will clear all contents after stop.
* string fillMode; - defaults to Forward,optional Forward / Backward,fillMode = Forward,Animation will pause on last frame while finished,fillMode = Backward , Animation will pause on first frame.
#### Methods
* constructor (url: string, autoPlay: boolean); - first params could be '#id' or CanvasHTMLElement
* startAnimation(reverse: boolean); - start animation from zero frame.
* startAnimationWithRange(range: {location: number, length: number}, reverse: boolean = false); - start animation in [location, location+length] frame range.
* pauseAnimation(); - pause animation on current frame.
* stopAnimation(); - stop animation, clear contents while clearsAfterStop === true
* setContentMode(mode: "ScaleToFill" | "AspectFill" | "AspectFit"); - Specific Scale Mode
* setClipsToBounds(clipsToBounds: boolean); - Clips if image render out of box.
* clear(); - force clear contents.
* stepToFrame(frame: int, andPlay: Boolean); - stop to specific frame, play animation while andPlay === true
* stepToPercentage(percentage: float, andPlay: Boolean); - stop to specific percentage, play animation while andPlay === true
* setImage(image: string, forKey: string, transform: [a, b, c, d, tx, ty]); - Replace Animation Images Dynamically, transform is optional, transform could adjust replacing image.
* setText(text: string | {text: string, font: string, size: string, color: string, offset: {x: float, y: float}}, forKey: string); - Add Text on Animation Image Dynamically
* clearDynamicObjects(); - clear all dynamic objects.
#### Callback Method
* onError(callback: (error: Error) => void): void; - call after load failure.
* onFinished(callback: () => void): void; - call after animation stop.
* onFrame(callback: (frame: number): void): void; - call after animation specific frame rendered.
* onPercentage(callback: (percentage: number): void): void; - call after animation specific percentage rendered.
# SVGAPlayer-Web
预览svga动画网址 http://svga.io/svga-preview.html
### 支持 SVGA-Format 1.x 格式
如果你需要播放 1.x 格式的 SVGA 文件,需要添加 JSZip 到你的 HTML 页面中。
```html
<script src="//s1.yy.com/ued_web_static/lib/jszip/3.1.4/??jszip.min.js,jszip-utils.min.js" charset="utf-8"></script>
```
#白鹭引擎使用 在白鹭的js后加下面脚本
<script src="libs/svga.egret.min.js"></script>
```js
var parser = new SVGA.Parser();
parser.load("../egret/loading五神兽最终版.svga", (videoItem) => {
//具体用法SVGA.d.ts文件有
var mv = new SVGA.EgretMovieClip(videoItem)
// mv.lockStep=true;
// mv.play()
// mv.gotoAndPlay(30, true)
// mv.gotoAndStop(10)
// mv.startAniRange
mv.gotoAndPlay(1, true)
this.addChild(mv);
let fun
mv.addEventListener(egret.Event.COMPLETE, fun = function () {
console.log("播放完成")
// mv.removeEventListener(egret.Event.COMPLETE,fun,this)
// mv.stop()
// mv.startAniRange(30,60);
}, this)
}, function (error) {
alert(error.message);
})
## 使用指南
canvas形式使用
### 手动加载
添加 ```<script src="https://cdn.jsdelivr.net/npm/svgaplayerweb@2.3.0/build/svga.min.js"></script>``` your.html 页面
你可以自行创建 Player Parser 并加载动画
1. 添加 Div 容器
```html
<div id="demoCanvas" style="styles..."></div>
```
2. 加载动画
```js
var player = new SVGA.Player('#demoCanvas');
var parser = new SVGA.Parser('#demoCanvas'); // 如果你需要支持 IE6+,那么必须把同样的选择器传给 Parser。
parser.load('rose_2.0.0.svga', function(videoItem) {
player.setVideoItem(videoItem);
player.startAnimation();
})
```
### 自动加载
为 canvas 元素添加以下属性
```html
<div src="rose_2.0.0.svga" loops="0" clearsAfterStop="true" style="styles..."></div>
```
动画会在页面加载完成后播放
### 动态图像
你可以动态替换动画中的指定元素,询问你的动画设计师以获取 ImageKey。
* 用于替换的图片,宽、高必须与原图一致。
* setImage 操作必须在 startAnimation 之前执行。
```
player.setImage('http://yourserver.com/xxx.png', 'ImageKey');
```
### 动态文本
你可以在指定元素上添加文本,询问你的动画设计师以获取 ImageKey。
* setText 操作必须在 startAnimation 之前执行。
```
player.setText('Hello, World!', 'ImageKey');
```
```
player.setText({
text: 'Hello, World!,
size: "24px",
color: "#ffe0a4",
offset: {x: 0.0, y: 0.0}
}, 'ImageKey'); // 可自定义文本样式
```
## Classes
### SVGA.Player
SVGA.Player 用于控制动画的播放和停止
#### Properties
* int loops; - 动画循环次数,默认值为 0,表示无限循环。
* BOOL clearsAfterStop; - 默认值为 true,表示当动画结束时,清空画布。
* string fillMode; - 默认值为 Forward,可选值 Forward / Backward,当 clearsAfterStop 为 false 时,Forward 表示动画会在结束后停留在最后一帧,Backward 则会在动画结束后停留在第一帧。
#### Methods
* constructor (canvas); - 传入 #id 或者 CanvasHTMLElement 至第一个参数
* startAnimation(reverse: boolean = false); - 从第 0 帧开始播放动画
* startAnimationWithRange(range: {location: number, length: number}, reverse: boolean = false); - 播放 [location, location+length] 指定区间帧动画
* pauseAnimation(); - 暂停在当前帧
* stopAnimation(); - 停止播放动画,如果 clearsAfterStop === true,将会清空画布
* setContentMode(mode: "ScaleToFill" | "AspectFill" | "AspectFit"); - 设置动画的拉伸模式
* setClipsToBounds(clipsToBounds: boolean); - 如果超出盒子边界,将会进行裁剪
* clear(); - 强制清空画布
* stepToFrame(frame: int, andPlay: Boolean); - 跳到指定帧,如果 andPlay === true,则在指定帧开始播放动画
* stepToPercentage(percentage: float, andPlay: Boolean); - 跳到指定百分比,如果 andPlay === true,则在指定百分比开始播放动画
* setImage(image: string, forKey: string, transform: [a, b, c, d, tx, ty]); - 设定动态图像, transform 是可选的, transform 用于变换替换图片
* setText(text: string | {text: string, font: string, size: string, color: string, offset: {x: float, y: float}}, forKey: string); - 设定动态文本
* clearDynamicObjects(); - 清空所有动态图像和文本
#### Callback Method
* onFinished(callback: () => void): void; - 动画停止播放时回调
* onFrame(callback: (frame: number): void): void; - 动画播放至某帧后回调
* onPercentage(callback: (percentage: number): void): void; - 动画播放至某进度后回调
### SVGA.Parser
SVGA.Parser 用于加载远端或 Base64 动画,并转换成 VideoItem。
跨域的 SVGA 资源需要使用 CORS 协议才能加载成功。
如果你需要加载 Base64 资源,或者 File 资源,这样传递就可以了 ```load(File)``` 或 ```load('data:svga/2.0;base64,xxxxxx')```。
#### Methods
* constructor();
* load(url: string, success: (videoItem: VideoEntity) => void, failure: (error: Error) => void): void;
## Issues
### Android 4.x 加载失败
某些 Android 4.x OS 上缺少 Blob 支持,请自行添加 Polyfill。
```
<script src="//cdn.bootcss.com/blob-polyfill/1.0.20150320/Blob.min.js"></script>
```
export as namespace SVGA;
declare global {
const SVGA: {
Parser: typeof Parser,
EgretMovieClip: typeof EgretMovieClip,
}
}
export class VideoEntity {
videoSize: { width: number, height: number }
FPS: number
frames: number
}
export class Parser {
load(url: string, success: (videoItem: VideoEntity) => void, failure?: (err: Error) => void): void
}
export class EgretMovieClip {
lockStep: boolean;
readonly currentFrame: number;
readonly isPlaying: boolean;
readonly isFront: boolean;
totalFrames: number;
/**
* 停止
*/
stop(): void;
/**
* 播放
*/
play(): void;
nextFrame(): void;
prevFrame(): void;
/**
* 停在指定帧
* @param frameIndex
*/
gotoAndStop(frameIndex: number): void;
/**
*
* @param frameIndex 1开始
* @param isFront 默认true正向播放
*/
gotoAndPlay(frameIndex: number, isFront: boolean): void;
readonly isInTimeFrame: boolean;
/**
* 帧数范围播放
* @param beginFrame 默认第一帧
* @param endFrame 默认最后一帧
* @param loops 默认0,表示无限循环
* @param callback 所有播放完的回调
*/
startAniRange(beginFrame: number, endFrame: number, loops: number, callback?: Function): void
constructor(mv: VideoEntity)
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
export as namespace SVGA;
declare global {
const SVGA: {
VideoEntity: typeof VideoEntity,
Parser: typeof Parser,
Player: typeof Player,
CreatejsPlayer: typeof CreatejsPlayer,
LayaboxPlayer: typeof LayaboxPlayer,
}
}
export class VideoEntity {
videoSize: { width: number, height: number }
FPS: number
frames: number
}
export class Parser {
load(url: string, success: (videoItem: VideoEntity) => void, failure?: (err: Error) => void): void
}
export class Player {
loops: number;
clearsAfterStop: boolean;
fillMode: "Forward" | "Backward"
constructor(canvas: string | HTMLCanvasElement | HTMLDivElement)
setVideoItem(videoItem: VideoEntity): void
setContentMode(contentMode: "Fill" | "AspectFill" | "AspectFit"): void
setClipsToBounds(clipsToBounds: boolean): void
startAnimation(reverse?: boolean): void
startAnimationWithRange(range: {location: number, length: number}, reverse?: boolean): void
pauseAnimation(): void
stopAnimation(clear?: boolean): void
clear(): void
stepToFrame(frame: number, andPlay?: boolean): void
stepToPercentage(percentage: number, andPlay?: boolean): void
setImage(urlORbase64: string, forKey: string, transform?: number[]): void
setText(textORMap: string | {
text: string,
size?: string,
family?: string,
color?: string,
offset?: { x: number, y: number }
}, forKey: string): void
clearDynamicObjects(): void
onFinished(callback: () => void): void
onFrame(callback: (frame: number) => void): void
onPercentage(callback: (percentage: number) => void): void
drawOnContext(ctx: CanvasRenderingContext2D, x: number, y: number, width?: number, height?: number): void
}
\ No newline at end of file
This diff is collapsed.
{
"name": "svgaplayerweb",
"version": "2.3.0",
"repository": {
"type": "git",
"url": "https://github.com/yyued/SVGAPlayer-Web.git"
},
"main": "./build/svga.min.js",
"types": "index.d.ts",
"scripts": {
"start": "http-server ./ & webpack -w & open http://127.0.0.1:8080/tests/",
"build": "webpack"
},
"author": "YYUED",
"license": "Apache License 2.0",
"dependencies": {},
"devDependencies": {
"pako": "^1.0.6",
"protobufjs": "^6.8.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"http-server": "^0.10.0",
"uglify-js": "^3.1.2",
"value-animator": "git+https://github.com/yyued/value-animator.git",
"webpack": "^3.6.0"
}
}
import { Parser } from '../parser'
import { Player } from './player'
export class AutoLoader {
static sharedParser = new Parser();
static autoload = (element, customParser) => {
if (typeof window === "undefined" || typeof document === "undefined") {
return;
}
let parser = customParser || AutoLoader.sharedParser;
if (element) {
if ((element.tagName === "CANVAS" || element.tagName === "DIV") && element.attributes.src && element.attributes.src.value.indexOf(".svga") === element.attributes.src.value.length - 5) {
let src = element.attributes.src.value;
let player = new Player(element);
parser.load(src, (videoItem) => {
if (element.attributes.loops) {
let loops = parseFloat(element.attributes.loops.value) || 0;
player.loops = loops;
}
if (element.attributes.clearsAfterStop) {
let clearsAfterStop = !(element.attributes.clearsAfterStop.value === "false")
player.clearsAfterStop = clearsAfterStop;
}
player.setVideoItem(videoItem);
player.startAnimation();
});
element.player = player;
}
}
else {
var elements = document.querySelectorAll('[src$=".svga"]');
for (var index = 0; index < elements.length; index++) {
var element = elements[index];
AutoLoader.autoload(element);
}
}
}
}
\ No newline at end of file
import { Parser } from '../parser'
import { Player } from './player'
import { AutoLoader } from './autoLoader'
module.exports = {
Parser,
Player,
autoload: AutoLoader.autoload,
}
AutoLoader.autoload();
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import { Parser } from '../parser'
import { MovieClip } from './MovieClip'
module.exports = {
Parser,
// Player,
EgretMovieClip: MovieClip,
}
\ No newline at end of file
export class BezierPath {
_d;
_transform;
_styles;
_shape;
constructor(d, transform, styles) {
this._d = d;
this._transform = transform;
this._styles = styles;
}
}
\ No newline at end of file
import { BezierPath } from './bezierPath'
export class EllipsePath extends BezierPath {
_x;
_y;
_radiusX;
_radiusY;
_transform;
_styles;
constructor(x, y, radiusX, radiusY, transform, styles) {
super();
this._x = x;
this._y = y;
this._radiusX = radiusX;
this._radiusY = radiusY;
this._transform = transform;
this._styles = styles;
}
}
\ No newline at end of file
import { BezierPath } from './bezierPath'
export class FrameEntity {
alpha = 0.0;
transform = {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
tx: 0.0,
ty: 0.0,
};
layout = {
x: 0.0,
y: 0.0,
width: 0.0,
height: 0.0,
}
nx = 0.0;
ny = 0.0;
/**
* BezierPath
*/
maskPath = null;
/**
* Object[]
*/
shapes = [];
constructor(spec) {
this.alpha = parseFloat(spec.alpha) || 0.0;
if (spec.layout) {
this.layout.x = parseFloat(spec.layout.x) || 0.0;
this.layout.y = parseFloat(spec.layout.y) || 0.0;
this.layout.width = parseFloat(spec.layout.width) || 0.0;
this.layout.height = parseFloat(spec.layout.height) || 0.0;
}
if (spec.transform) {
this.transform.a = parseFloat(spec.transform.a) || 1.0;
this.transform.b = parseFloat(spec.transform.b) || 0.0;
this.transform.c = parseFloat(spec.transform.c) || 0.0;
this.transform.d = parseFloat(spec.transform.d) || 1.0;
this.transform.tx = parseFloat(spec.transform.tx) || 0.0;
this.transform.ty = parseFloat(spec.transform.ty) || 0.0;
}
if (spec.clipPath && spec.clipPath.length > 0) {
this.maskPath = new BezierPath(spec.clipPath, undefined, { fill: "#000000" });
}
if (spec.shapes) {
if (spec.shapes instanceof Array) {
spec.shapes.forEach(shape => {
shape.pathArgs = shape.args;
switch (shape.type) {
case 0:
shape.type = "shape";
shape.pathArgs = shape.shape;
break;
case 1:
shape.type = "rect";
shape.pathArgs = shape.rect;
break;
case 2:
shape.type = "ellipse";
shape.pathArgs = shape.ellipse;
break;
case 3:
shape.type = "keep";
break;
}
if (shape.styles) {
if (shape.styles.fill) {
if (typeof shape.styles.fill["r"] === "number") shape.styles.fill[0] = shape.styles.fill["r"];
if (typeof shape.styles.fill["g"] === "number") shape.styles.fill[1] = shape.styles.fill["g"];
if (typeof shape.styles.fill["b"] === "number") shape.styles.fill[2] = shape.styles.fill["b"];
if (typeof shape.styles.fill["a"] === "number") shape.styles.fill[3] = shape.styles.fill["a"];
}
if (shape.styles.stroke) {
if (typeof shape.styles.stroke["r"] === "number") shape.styles.stroke[0] = shape.styles.stroke["r"];
if (typeof shape.styles.stroke["g"] === "number") shape.styles.stroke[1] = shape.styles.stroke["g"];
if (typeof shape.styles.stroke["b"] === "number") shape.styles.stroke[2] = shape.styles.stroke["b"];
if (typeof shape.styles.stroke["a"] === "number") shape.styles.stroke[3] = shape.styles.stroke["a"];
}
let lineDash = shape.styles.lineDash || []
if (shape.styles.lineDashI > 0) {
lineDash.push(shape.styles.lineDashI)
}
if (shape.styles.lineDashII > 0) {
if (lineDash.length < 1) { lineDash.push(0) }
lineDash.push(shape.styles.lineDashII)
lineDash.push(0)
}
if (shape.styles.lineDashIII > 0) {
if (lineDash.length < 2) { lineDash.push(0); lineDash.push(0); }
lineDash[2] = shape.styles.lineDashIII
}
shape.styles.lineDash = lineDash
switch (shape.styles.lineJoin) {
case 0:
shape.styles.lineJoin = "miter";
break;
case 1:
shape.styles.lineJoin = "round";
break;
case 2:
shape.styles.lineJoin = "bevel";
break;
}
switch (shape.styles.lineCap) {
case 0:
shape.styles.lineCap = "butt";
break;
case 1:
shape.styles.lineCap = "round";
break;
case 2:
shape.styles.lineCap = "square";
break;
}
}
})
}
if (spec.shapes[0] && spec.shapes[0].type === "keep") {
this.shapes = FrameEntity.lastShapes;
}
else {
this.shapes = spec.shapes
FrameEntity.lastShapes = spec.shapes;
}
}
let llx = this.transform.a * this.layout.x + this.transform.c * this.layout.y + this.transform.tx;
let lrx = this.transform.a * (this.layout.x + this.layout.width) + this.transform.c * this.layout.y + this.transform.tx;
let lbx = this.transform.a * this.layout.x + this.transform.c * (this.layout.y + this.layout.height) + this.transform.tx;
let rbx = this.transform.a * (this.layout.x + this.layout.width) + this.transform.c * (this.layout.y + this.layout.height) + this.transform.tx;
let lly = this.transform.b * this.layout.x + this.transform.d * this.layout.y + this.transform.ty;
let lry = this.transform.b * (this.layout.x + this.layout.width) + this.transform.d * this.layout.y + this.transform.ty;
let lby = this.transform.b * this.layout.x + this.transform.d * (this.layout.y + this.layout.height) + this.transform.ty;
let rby = this.transform.b * (this.layout.x + this.layout.width) + this.transform.d * (this.layout.y + this.layout.height) + this.transform.ty;
this.nx = Math.min(Math.min(lbx, rbx), Math.min(llx, lrx));
this.ny = Math.min(Math.min(lby, rby), Math.min(lly, lry));
}
}
\ No newline at end of file
const { ProtoMovieEntity } = require("./proto")
const assignUtils = require('pako/lib/utils/common').assign;
const inflate = require("pako/lib/inflate")
const pako = {}
assignUtils(pako, inflate);
const Uint8ToString = function (u8a) {
var CHUNK_SZ = 0x8000;
var c = [];
for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
}
return c.join("");
}
const actions = {
loadAssets: (url, cb, failure) => {
if (typeof JSZipUtils === "object" && typeof JSZip === "function") {
if (url.toString() == "[object File]"){
actions._readBlobAsArrayBuffer(url, function (arrayBufferSVGA) {
const dataHeader = new Uint8Array(arrayBufferSVGA, 0, 4)
if (dataHeader[0] == 80 && dataHeader[1] == 75 && dataHeader[2] == 3 && dataHeader[3] == 4) {
JSZip.loadAsync(arrayBufferSVGA).then(function (zip) {
actions._decodeAssets(zip, cb);
});
}
else {
actions.load_viaProto(arrayBufferSVGA, cb, failure);
}
});
} else if(url.indexOf("data:svga/1.0;base64,") >= 0) {
var arrayBufferSVGA = actions._base64ToArrayBuffer(url.substring(21));
JSZip.loadAsync(arrayBufferSVGA).then(function (zip) {
actions._decodeAssets(zip, cb);
});
}else if(url.indexOf("data:svga/2.0;base64,") >= 0){
var arrayBufferSVGA = actions._base64ToArrayBuffer(url.substring(21));
actions.load_viaProto(arrayBufferSVGA, cb, failure);
}else{
JSZipUtils.getBinaryContent(url, function (err, data) {
if (err) {
failure && failure(err);
console.error(err);
throw err;
}
else {
const dataHeader = new Uint8Array(data, 0, 4)
if (dataHeader[0] == 80 && dataHeader[1] == 75 && dataHeader[2] == 3 && dataHeader[3] == 4) {
JSZip.loadAsync(data).then(function (zip) {
actions._decodeAssets(zip, cb);
});
}
else {
actions.load_viaProto(data, cb, failure);
}
}
});
}
}
else {
const req = new XMLHttpRequest()
req.open("GET", url, true);
req.responseType = "arraybuffer"
req.onloadend = () => {
actions.load_viaProto(req.response, cb, failure);
}
req.send()
}
},
load_viaProto: (arraybuffer, cb, failure) => {
try {
const inflatedData = pako.inflate(arraybuffer);
const movieData = ProtoMovieEntity.decode(inflatedData);
let images = {};
actions._loadImages(images, undefined, movieData, function () {
movieData.ver = "2.0";
cb({
movie: movieData,
images,
})
})
} catch (err) {
failure && failure(err);
console.error(err);
throw err;
}
},
_decodeAssets: (zip, cb) => {
var version = "1.0";
if(zip.file("movie.binary")){
version = "1.5";
}
zip.file("movie.spec").async("string").then(function (spec) {
let movieData = JSON.parse(spec);
let images = {};
movieData.ver = version;
actions._loadImages(images, zip, movieData, function () {
cb({
movie: movieData,
images,
})
})
})
},
_loadImages: function (images, zip, movieData, imagesLoadedBlock) {
if (typeof movieData === "object" && movieData.$type == ProtoMovieEntity) {
var finished = true;
if (!zip) {
for (const key in movieData.images) {
if (movieData.images.hasOwnProperty(key)) {
const element = movieData.images[key];
const value = Uint8ToString(element);
images[key] = btoa(value)
}
}
}
else {
for (const key in movieData.images) {
if (movieData.images.hasOwnProperty(key)) {
const element = movieData.images[key];
const value = Uint8ToString(element);
if (images.hasOwnProperty(key)) {
continue;
}
finished = false;
zip.file(value + ".png").async("base64").then(function (data) {
images[key] = data;
actions._loadImages(images, zip, movieData, imagesLoadedBlock);
}.bind(this))
break;
}
}
}
finished && imagesLoadedBlock.call(this)
}
else {
var finished = true;
for (var key in movieData.images) {
if (movieData.images.hasOwnProperty(key)) {
var element = movieData.images[key];
if (images.hasOwnProperty(key)) {
continue;
}
finished = false;
zip.file(element + ".png").async("base64").then(function (data) {
images[key] = data;
actions._loadImages(images, zip, movieData, imagesLoadedBlock);
}.bind(this))
break;
}
}
finished && imagesLoadedBlock.call(this)
}
},
_base64ToArrayBuffer: (base64) => {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
},
_readBlobAsArrayBuffer: (blob, callback) => {
var reader = new FileReader();
reader.onload = function(e) {callback(e.target.result);};
reader.readAsArrayBuffer(blob);
}
}
module.exports = (data, cb, failure) => {
actions['loadAssets'](data, cb, failure);
}
import { VideoEntity } from './videoEntity'
import MockWorker from './mockWorker'
export class Parser {
/**
* url: 资源路径
* success(VideoEntity videoItem)
*/
load(url, success, failure) {
this.loadViaWorker(url, success, failure);
}
loadViaWorker(url, success, failure) {
MockWorker(url, (data) => {
let movie = data.movie;
movie["version"] = data.ver;
let images = data.images;
let videoItem = new VideoEntity(movie, images);
success(videoItem);
}, failure)
}
}
\ No newline at end of file
const protobuf = require("protobufjs/light")
const svgaDescriptor = JSON.parse(`{"nested":{"com":{"nested":{"opensource":{"nested":{"svga":{"options":{"objc_class_prefix":"SVGAProto","java_package":"com.opensource.svgaplayer.proto"},"nested":{"MovieParams":{"fields":{"viewBoxWidth":{"type":"float","id":1},"viewBoxHeight":{"type":"float","id":2},"fps":{"type":"int32","id":3},"frames":{"type":"int32","id":4}}},"SpriteEntity":{"fields":{"imageKey":{"type":"string","id":1},"frames":{"rule":"repeated","type":"FrameEntity","id":2},"matteKey":{"type":"string","id":3}}},"AudioEntity":{"fields":{"audioKey":{"type":"string","id":1},"startFrame":{"type":"int32","id":2},"endFrame":{"type":"int32","id":3},"startTime":{"type":"int32","id":4},"totalTime":{"type":"int32","id":5}}},"Layout":{"fields":{"x":{"type":"float","id":1},"y":{"type":"float","id":2},"width":{"type":"float","id":3},"height":{"type":"float","id":4}}},"Transform":{"fields":{"a":{"type":"float","id":1},"b":{"type":"float","id":2},"c":{"type":"float","id":3},"d":{"type":"float","id":4},"tx":{"type":"float","id":5},"ty":{"type":"float","id":6}}},"ShapeEntity":{"oneofs":{"args":{"oneof":["shape","rect","ellipse"]}},"fields":{"type":{"type":"ShapeType","id":1},"shape":{"type":"ShapeArgs","id":2},"rect":{"type":"RectArgs","id":3},"ellipse":{"type":"EllipseArgs","id":4},"styles":{"type":"ShapeStyle","id":10},"transform":{"type":"Transform","id":11}},"nested":{"ShapeType":{"values":{"SHAPE":0,"RECT":1,"ELLIPSE":2,"KEEP":3}},"ShapeArgs":{"fields":{"d":{"type":"string","id":1}}},"RectArgs":{"fields":{"x":{"type":"float","id":1},"y":{"type":"float","id":2},"width":{"type":"float","id":3},"height":{"type":"float","id":4},"cornerRadius":{"type":"float","id":5}}},"EllipseArgs":{"fields":{"x":{"type":"float","id":1},"y":{"type":"float","id":2},"radiusX":{"type":"float","id":3},"radiusY":{"type":"float","id":4}}},"ShapeStyle":{"fields":{"fill":{"type":"RGBAColor","id":1},"stroke":{"type":"RGBAColor","id":2},"strokeWidth":{"type":"float","id":3},"lineCap":{"type":"LineCap","id":4},"lineJoin":{"type":"LineJoin","id":5},"miterLimit":{"type":"float","id":6},"lineDashI":{"type":"float","id":7},"lineDashII":{"type":"float","id":8},"lineDashIII":{"type":"float","id":9}},"nested":{"RGBAColor":{"fields":{"r":{"type":"float","id":1},"g":{"type":"float","id":2},"b":{"type":"float","id":3},"a":{"type":"float","id":4}}},"LineCap":{"values":{"LineCap_BUTT":0,"LineCap_ROUND":1,"LineCap_SQUARE":2}},"LineJoin":{"values":{"LineJoin_MITER":0,"LineJoin_ROUND":1,"LineJoin_BEVEL":2}}}}}},"FrameEntity":{"fields":{"alpha":{"type":"float","id":1},"layout":{"type":"Layout","id":2},"transform":{"type":"Transform","id":3},"clipPath":{"type":"string","id":4},"shapes":{"rule":"repeated","type":"ShapeEntity","id":5}}},"MovieEntity":{"fields":{"version":{"type":"string","id":1},"params":{"type":"MovieParams","id":2},"images":{"keyType":"string","type":"bytes","id":3},"sprites":{"rule":"repeated","type":"SpriteEntity","id":4},"audios":{"rule":"repeated","type":"AudioEntity","id":5}}}}}}}}}}}`)
export const proto = protobuf.Root.fromJSON(svgaDescriptor);
export const ProtoMovieEntity = proto.lookupType("com.opensource.svga.MovieEntity");
\ No newline at end of file
import { BezierPath } from './bezierPath'
export class RectPath extends BezierPath {
_x;
_y;
_width;
_height;
_cornerRadius;
_transform;
_styles;
constructor(x, y, width, height, cornerRadius, transform, styles) {
super();
this._x = x
this._y = y
this._width = width
this._height = height
this._cornerRadius = cornerRadius
this._transform = transform
this._styles = styles
}
}
\ No newline at end of file
import { FrameEntity } from './frameEntity'
import { BezierPath } from './bezierPath'
import { RectPath } from './rectPath'
import { EllipsePath } from './ellipsePath'
export class SpriteEntity {
/**
* string
*/
matteKey = null
/**
* string
*/
imageKey = null
/**
* FrameEntity[]
*/
frames = []
constructor(spec) {
this.matteKey = spec.matteKey;
this.imageKey = spec.imageKey;
if (spec.frames) {
this.frames = spec.frames.map((obj) => {
return new FrameEntity(obj)
})
}
}
}
\ No newline at end of file
import { SpriteEntity } from './spriteEntity'
const { ProtoMovieEntity } = require("./proto")
export class VideoEntity {
/**
* SVGA 文件版本
*/
version = "";
/**
* 影片尺寸
*/
videoSize = {
width: 0.0,
height: 0.0,
};
/**
* 帧率
*/
FPS = 20;
/**
* 帧数
*/
frames = 0;
/**
* Bitmaps
*/
images = {};
/**
* SpriteEntity[]
*/
sprites = []
/**
* AudioEntity[]
*/
audios = []
constructor(spec, images) {
if (typeof spec === "object" && spec.$type == ProtoMovieEntity) {
if (typeof spec.params === "object") {
this.version = spec.ver;
this.videoSize.width = spec.params.viewBoxWidth || 0.0;
this.videoSize.height = spec.params.viewBoxHeight || 0.0;
this.FPS = spec.params.fps || 20;
this.frames = spec.params.frames || 0;
}
this.resetSprites(spec)
this.audios = spec.audios
}
else if (spec) {
if (spec.movie) {
if (spec.movie.viewBox) {
this.videoSize.width = parseFloat(spec.movie.viewBox.width) || 0.0;
this.videoSize.height = parseFloat(spec.movie.viewBox.height) || 0.0;
}
this.version = spec.ver;
this.FPS = parseInt(spec.movie.fps) || 20;
this.frames = parseInt(spec.movie.frames) || 0;
}
this.resetSprites(spec)
}
if (images) {
this.images = images
}
}
resetSprites(spec) {
if (spec.sprites instanceof Array) {
this.sprites = spec.sprites.map((obj) => {
return new SpriteEntity(obj)
})
}
}
}
\ No newline at end of file
This diff is collapsed.
<html>
<body style="text-align: center">
<div>
<div id="testCanvas" style="background-color: #000000; width: 500px; height: 500px; margin: auto"></div>
<!-- <canvas id="testCanvas" width="500" height="500" style="background-color: #000000; "></canvas> -->
</div>
<script src="https://cdn.jsdelivr.net/npm/howler@2.0.15/dist/howler.core.min.js"></script>
<!--[if lt IE 10]>
<script src="../build/svga.ie.min.js"></script>
<![endif]-->
<!--[if gte IE 10]><!-->
<!-- <script src="http://assets.dwstatic.com/common/lib/??jszip/3.1.3/jszip.min.js,jszip/3.1.3/jszip-utils.min.js"
charset="utf-8"></script> -->
<script src="../build/svga.min.js"></script>
<!--<![endif]-->
<script>
var parser = new SVGA.Parser('#testCanvas')
var player = new SVGA.Player('#testCanvas')
parser.load("./samples/angel.svga", function (videoItem) {
// player.setImage('./samples/avatar.png', '99')
player.setVideoItem(videoItem);
player.startAnimation();
// player.startAnimationWithRange({location: 20, length: 1}, false);
}, function (error) {
alert(error.message);
})
</script>
</body>
</html>
\ No newline at end of file
<html>
<body style="text-align: center">
<div id="test">
<canvas id="demoCanvas" width="750" height="750" style="background-color: #000000"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/createjs@1.0.1/builds/1.0.0/createjs.min.js"></script>
<script src="http://assets.dwstatic.com/common/lib/??jszip/3.1.3/jszip.min.js,jszip/3.1.3/jszip-utils.min.js" charset="utf-8"></script>
<script src="../build/svga.createjs.min.js"></script>
<script>
var sprite = new SVGA.Player('./samples/angel.svga');
sprite.onError((err) => {
console.error(err)
})
var stage = new createjs.Stage('demoCanvas');
sprite.setFrame(0, 0, 750, 750);
// sprite.setContentMode("AspectFill")
stage.addChild(sprite);
stage.update();
</script>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
<html>
<body style="text-align: center">
<div>
<canvas id="drawingCanvas" width="500" height="1000" style="background-color: #000000; "></canvas>
</div>
<script src="http://assets.dwstatic.com/common/lib/??jszip/3.1.3/jszip.min.js,jszip/3.1.3/jszip-utils.min.js" charset="utf-8"></script>
<script src="../build/svga.min.js"></script>
<script>
var parser = new SVGA.Parser()
var player = new SVGA.Player()
parser.load("./samples/rose.svga", function (videoItem) {
player.setVideoItem(videoItem);
player.startAnimation()
})
const requestFrame = () => {
requestAnimationFrame(() => {
const ctx = document.getElementById('drawingCanvas').getContext('2d');
ctx.clearRect(0, 0, 500, 1000);
ctx.fillStyle = "#e2e2e2"
ctx.fillRect(0, 0, 500, 1000);
player.drawOnContext(ctx, 100, 100, 250, 250);
requestFrame()
})
}
requestFrame();
</script>
</body>
</html>
\ No newline at end of file
# SVGA-Samples
以下动画版权归 YY Inc. 所有,仅用于学习使用,禁止商业使用。
* angel.svga
* halloween.svga
* kingset.svga
* posche.svga
* rose.svga
var path = require('path');
var webpack = require('webpack')
module.exports = {
entry: {
"svga.min": "./src/Canvas/index.js",
"svga.egret.min":"./src/Egret/index.js",
},
output: {
path: __dirname,
filename: "build/[name].js",
libraryTarget: 'umd',
library: 'SVGA',
},
module: {
loaders: [
{
test: path.join(__dirname, 'src'),
loader: 'babel-loader',
query: {
presets: ['es2015', "stage-0"]
}
}
],
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
include: /\.min\.js$/,
minimize: true,
output: { comments: false },
})
],
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment