Commit 5b915d1d authored by rockyl's avatar rockyl

砖块0的情况

parents
Pipeline #109299 failed with stages
in 0 seconds
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
/.rpt2_cache/
{
"name": "scilla",
"version": "1.0.0",
"main": "./dist/bundle.js",
"types": "./types/index.d.ts",
"license": "MIT",
"scripts": {
"build": "rollup -c"
},
"dependencies": {
"tslib": "^1.9.3"
},
"devDependencies": {
"rollup": "^0.66.6",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-typescript2": "^0.18.0",
"rollup-plugin-uglify": "^6.0.0",
"tslib": "^1.9.3",
"typescript": "^3.1.6"
}
}
/**
* Created by rockyl on 2018/11/16.
*/
const resolve = require('rollup-plugin-node-resolve');
const commonjs = require('rollup-plugin-commonjs');
const typescript = require('rollup-plugin-typescript2');
const {uglify} = require('rollup-plugin-uglify');
export default {
input: 'src/index.ts',
output: {
file: 'dist/bundle.js',
format: 'umd',
name: 'scilla',
//sourcemap: true,
},
plugins: [
resolve({
browser: true,
}),
typescript({
typescript: require('typescript'),
tslib: require('tslib'),
useTsconfigDeclarationDir: true,
}),
commonjs(),
uglify({}),
]
};
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
export type color = string;
export type resource = any;
export type raw = any;
This diff is collapsed.
This diff is collapsed.
/**
* Created by rockyl on 2018-11-30.
*/
import {Sheet} from "./Sheet";
const animationMap = {};
const animDataMap = {};
const textureMap = {};
/**
* 获取一个帧动画资源
* @param name
*/
export function getFrameAnimation(name): FrameAnimation {
let animation: FrameAnimation = animationMap[name];
if (!animation) {
animation = animationMap[name] = new FrameAnimationImpl(name);
animation.fillMcData(name);
}
return animation;
}
/**
* 放置帧动画图片和数据
* @param img
* @param data
*/
export function putFrameAnim(img, data) {
const {mc, res} = data;
let sheet: Sheet = new Sheet(img, res);
for (let key in mc) {
const animData = animDataMap[key] = mc[key];
animData.sheet = sheet;
}
}
/**
* 帧动画资源
*/
export interface FrameAnimation {
/**
* 填充帧数据
* @param name
*/
fillMcData(name);
/**
* 获取帧率
*/
readonly fps: number;
/**
* 获取所有帧标签
*/
readonly labels: any[];
/**
* 获取帧数
*/
readonly frameCount: number;
/**
* 根据名字获取帧标签
* @param name
*/
getLabel(name): any;
/**
* 获取帧
* @param frameIndex
*/
getFrame(frameIndex): any;
/**
* 销毁自身
*/
destroy()
}
/**
* 帧动画资源实现
*/
export class FrameAnimationImpl implements FrameAnimation {
private readonly _name: string;
private _animData;
constructor(name) {
this._name = name;
}
get name(): string {
return this._name;
}
fillMcData(name) {
const animData = animDataMap[name];
if (animData) {
this._animData = animData;
} else {
console.warn(`anim data [${name}] is not exist`)
}
}
get fps(): number {
return this._animData.frameRate;
}
get labels(): any[] {
return this._animData.labels;
}
get frameCount(): number {
return this._animData.frames.length;
}
getLabel(name): any {
let result;
for (let label of this._animData.labels) {
if (label.name == name) {
result = label;
break;
}
}
return result;
}
getFrame(frameIndex): any {
const {_animData,} = this;
let texture, frameData;
if (_animData) {
const {frames} = _animData;
frameData = frames[frameIndex];
if (frameData) {
const res = frameData.res;
texture = textureMap[res];
if (!texture) {
texture = textureMap[res] = _animData.sheet.getTexture(res);
}
}
}
return {
texture,
data: frameData,
};
}
destroy() {
this._animData = null;
}
}
/**
* Created by rockyl on 2018/11/5.
*/
let HASH_CODE_INK = 0;
function getHashCode() {
return ++HASH_CODE_INK;
}
/**
* 哈希对象
*/
export default class HashObject {
_hashCode;
constructor() {
this._hashCode = getHashCode();
}
get hashCode() {
return this._hashCode;
}
}
/**
* Created by rockyl on 2018-12-03.
*/
import {Entity} from "./Entity";
import {loadResItems} from "../assets-manager";
export class Scene {
name: string;
root: Entity;
resourceGroups: any = {
preload: [],
delay: [],
};
config: any;
initByConfig(config){
this.config = config;
this.name = config.name;
const resourceGroups = config['resource-groups'];
for(let key in resourceGroups){
this.resourceGroups[key] = resourceGroups[key];
}
}
async loadResGroup(name, progress?){
await loadResItems(this.resourceGroups[name], progress);
}
}
/**
* Created by rockyl on 2018/11/5.
*/
import HashObject from "./HashObject";
import {Entity, } from "./Entity";
import {EngineConfig} from "../engine-config";
const interactiveMap = [
'_dealGlobalTouchBegin',
'_dealGlobalTouchMove',
'_dealGlobalTouchEnd',
];
/**
* 组件基类
*/
export class ScillaComponent extends HashObject {
/**
* 所依附的实体
*/
entity: Entity;
protected delayCallbacks = [];
private _firstUpdate: boolean;
//是否有效
protected _enabled: boolean = EngineConfig.componentEnabled;
/**
* 是否有效状态
*/
get enabled(): boolean {
return this._enabled;
}
set enabled(value: boolean) {
if (this._enabled !== value) {
this._enabled = value;
if (this.entity && this.entity.isActive) {
if (this._enabled) {
this.onEnable();
} else {
this.onDisable();
}
}
}
}
/**
* 装配实体
* @param entity
*/
_setup(entity: Entity) {
this.entity = entity;
this.onCreate();
}
/**
* 卸载实体
*/
_unSetup() {
this.entity = null;
this.onDestroy();
}
/**
* 当组件被创建时
*/
onCreate() {
}
/**
* 当组件被唤醒时
*/
onAwake() {
this._firstUpdate = true;
}
/**
* 当组件生效时
*/
onEnable(){
}
/**
* 当组件失效时
*/
onDisable(){
}
$onUpdate(t) {
this.onUpdate(t);
if(!this._firstUpdate){
this.invokeDelayCallback(t);
}
this._firstUpdate = false;
}
private invokeDelayCallback(t){
const removed = [];
if(this.entity.name.indexOf('progress') >= 0){
console.log();
}
for (let i = 0, li = this.delayCallbacks.length; i < li; i++) {
let {callback, once} = this.delayCallbacks[i];
if(once){
removed.push(i);
}
callback.call(this, t);
}
for(let item of removed){
this.delayCallbacks.splice(item, 1);
}
}
/**
* 当时钟更新时
* @param t 从引擎开始到当前的毫秒数
*/
onUpdate(t) {
}
/**
* 当子节点的时钟更新结束后
*/
afterUpdate() {
}
/**
* 当组件沉睡时
*/
onSleep() {
}
/**
* 当组件被销毁时
*/
onDestroy() {
}
/**
* 当被监听的属性被修改时
* @param value
* @param key
* @param oldValue
*/
protected onModify(value, key, oldValue){
}
private getDelayCallback(callback){
let result;
for(let item of this.delayCallbacks){
if(item.callback == callback){
result = item;
break;
}
}
return result;
}
/**
* 执行延迟回调
* @param callback
* @param once 是否只执行一次
*/
callOnNextTick(callback, once = true) {
const item = this.getDelayCallback(callback);
if (!item) {
this.delayCallbacks.push({callback, once});
}
}
cancelOnNextTick(callback){
const item = this.getDelayCallback(callback);
const index = this.delayCallbacks.indexOf(item);
if(index >= 0){
this.delayCallbacks.splice(index, 1);
}
}
/**
* 当交互时
* @param type
* @param event
*/
onInteract(type, event) {
try {
const hitOn = this[interactiveMap[type]](event);
return hitOn && this['touchInterrupt'];
} catch (e) {
console.warn(e);
}
}
_dealGlobalTouchBegin(e) {
return this.onGlobalTouchBegin(e);
}
_dealGlobalTouchMove(e) {
return this.onGlobalTouchMove(e);
}
_dealGlobalTouchEnd(e) {
return this.onGlobalTouchEnd(e);
}
/**
* 当全局触摸开始
* @param e
*/
onGlobalTouchBegin(e) {
}
/**
* 当全触摸移动
* @param e
*/
onGlobalTouchMove(e) {
}
/**
* 当全触摸结束
* @param e
*/
onGlobalTouchEnd(e) {
}
get transform(){
return this.entity.getComponent('components/base/Transform');
}
/**
* 向下广播
* 如果某组件调用后返回true,将结束整条链
* @param method 方法名
* @param level 深度,默认全部遍历
* @param params 参数
*/
broadcast(method, level = -1, ...params) {
this.entity.broadcast(method, level, ...params);
}
/**
* 向上冒泡
* 如果某组件调用后返回true,将结束整条链
* @param method 方法名
* @param params 参数
*/
bubbling(method, ...params) {
this.entity.bubbling(method, ...params);
}
}
/**
* Created by rockyl on 2018-11-27.
*/
/**
* 单一事件类
* 一对多形式的订阅分发机制
*/
export class ScillaEvent {
private _subscribers: any[];
constructor() {
this._subscribers = [];
}
private findListener(callback) {
const {_subscribers} = this;
let result;
for (let i = 0, li = _subscribers.length; i < li; i++) {
const subscriber = _subscribers[i];
if (subscriber.callback == callback) {
result = {
subscriber,
index: i,
};
break;
}
}
return result;
}
/**
* 添加侦听
* @param callback
* @param thisObj
* @param priority
* @param params
*/
addListener(callback, thisObj?, priority = 0, ...params) {
if (!callback) {
return;
}
const {_subscribers} = this;
const listener = this.findListener(callback);
if (!listener) {
_subscribers.push({
callback,
thisObj,
priority,
params,
});
}
}
/**
* 添加单次侦听
* @param callback
* @param thisObj
* @param priority
* @param params
*/
once(callback, thisObj?, priority = 0, ...params) {
if (!callback) {
return;
}
const {_subscribers} = this;
const listener = this.findListener(callback);
if (!listener) {
_subscribers.push({
callback,
thisObj,
priority,
params,
once: true,
});
}
}
/**
* 移除侦听
* @param callback
*/
removeListener(callback) {
if (!callback) {
return;
}
const {_subscribers} = this;
const listener = this.findListener(callback);
if (listener) {
_subscribers.splice(listener.index, 1);
}
}
/**
* 是否已经侦听
* @param callback
*/
hasListener(callback) {
return !!this.findListener(callback);
}
/**
* 调用派发
* @param paramsNew
*/
invoke(...paramsNew) {
const {_subscribers} = this;
//按优先级降序
_subscribers.sort((a, b) => {
return a.priority - b.priority;
});
for (const subscriber of _subscribers) {
if(subscriber){
const {callback, thisObj, once, params}= subscriber;
const allParams = params.concat(paramsNew);
try {
callback.apply(thisObj, allParams);
}catch (e) {
console.log(e);
}
if(once){
this.removeListener(callback);
}
}
}
}
}
/**
* Created by rockyl on 2018-11-30.
*/
import HashObject from "../core/HashObject";
import {createTexture} from "./Texture";
/**
* 图集
*/
export class Sheet extends HashObject{
/**
* 图集原图
*/
img: any;
/**
* 图集分割配置
*/
frames: any;
private _textureCache: any = {};
constructor(img?, frames?) {
super();
if(img){
this.img = img;
}
if(frames){
this.frames = frames;
}
}
/**
* 生成全部纹理
*/
generateAll() {
for (let key in this.frames) {
this.generateTexture(key);
}
}
/**
* 生成一个纹理
* @param name
* @param force
*/
generateTexture(name, force = false) {
const {img, frames, _textureCache} = this;
if (!force && _textureCache[name]) {
return _textureCache[name];
}
const frame = frames[name];
if (frame) {
return _textureCache[name] = createTexture(img, frame);
}
}
/**
* 是否有这个纹理
* @param name
*/
hasTexture(name) {
return !!frames[name];
}
/**
* 获取纹理
* @param name
*/
getTexture(name) {
let texture = this._textureCache[name];
if (texture) {
return texture;
} else {
return this.generateTexture(name);
}
}
/**
* 获取全部存在的纹理
*/
getAllTextures() {
return this._textureCache;
}
/**
* 销毁自身
*/
destroy() {
this.img = null;
for (let key in this._textureCache) {
this._textureCache[key].destroy();
delete this._textureCache[key];
}
}
}
/**
* Created by rockyl on 2018/7/12.
*/
import Bounds from "../support/Bounds";
import HashObject from "../core/HashObject";
import {createCanvas} from "./context/RenderContext";
/**
* 纹理类
*/
export default class Texture extends HashObject {
img: any;
bounds: Bounds;
_cacheCanvas;
constructor() {
super();
this.bounds = new Bounds();
}
/**
* 设置图集中的坐标和尺寸
* @param frame
*/
setFrame(frame) {
let {x, y, w, h} = frame;
this.bounds.setTo(x, y, w, h);
}
/**
* 设置图片
* @param img
*/
setImg(img) {
this.img = img;
}
/**
* 获取纹理宽度
*/
get width() {
return this.bounds.width;
}
/**
* 获取纹理高度
*/
get height() {
return this.bounds.height;
}
/**
* 产生一个缓存画布
*/
getCacheCanvas() {
const {width, height} = this.bounds;
let canvas = this._cacheCanvas;
if (!canvas) {
canvas = this._cacheCanvas = createCanvas();
}
canvas.width = width;
canvas.height = height;
const context = canvas.getContext('2d');
this.drawToCanvas(context);
return canvas;
}
/**
* 绘制到一个画布上
* @param context
* @param dx
* @param dy
* @param sx
* @param sy
* @param dw
* @param dh
*/
drawToCanvas(context, dx = 0, dy = 0, sx?, sy?, dw?, dh?) {
const {x, y, width, height} = this.bounds;
context.drawImage(this.img, sx || x, sy || y, width, height, dx, dy, dw || width, dh || height);
}
/**
* 销毁自身
*/
destroy() {
this.img = null;
this.bounds = null;
this.destroyCacheCanvas();
}
/**
* 销毁缓存画布
*/
destroyCacheCanvas(){
this._cacheCanvas = null;
}
}
/**
* 快捷创建纹理
* @param img
* @param frame
*/
export function createTexture(img, frame?): Texture {
const texture = new Texture();
texture.setImg(img);
texture.setFrame(frame || {x: 0, y: 0, w: img.width, h: img.height});
return texture;
}
/**
* Created by rockyl on 2018/11/7.
*
* 交互上下文
*/
let _canvas;
let _touchHandler;
let _scaleX, _scaleY, _rotation;
const ua = navigator.userAgent.toLowerCase();
const isMobile = (ua.indexOf('mobile') !== -1 || ua.indexOf('android') !== -1);
/**
* 装配上下文
* @param options
*/
export function setupContext(options:any = {}) {
const {canvas, touchHandler} = options;
_touchHandler = touchHandler;
_canvas = canvas;
addListeners();
}
/**
* 更新缩放模式
* @param scaleX
* @param scaleY
* @param rotation
*/
export function updateScaleMode(scaleX, scaleY, rotation) {
_scaleX = scaleX;
_scaleY = scaleY;
_rotation = rotation;
}
/**
* 适配鼠标事件
*/
function addListeners() {
if (window.navigator.msPointerEnabled) {
_canvas.addEventListener("MSPointerDown", (event) => {
event.identifier = event.pointerId;
onTouchBegin(event);
prevent(event);
}, false);
_canvas.addEventListener("MSPointerMove", (event) => {
event.identifier = event.pointerId;
onTouchMove(event);
prevent(event);
}, false);
_canvas.addEventListener("MSPointerUp", (event) => {
event.identifier = event.pointerId;
onTouchEnd(event);
prevent(event);
}, false);
}
else {
if (!isMobile) {
addMouseListener();
}
addTouchListener();
}
}
/**
* 阻断页面拖动
* @param event
*/
function prevent(event) {
event.stopPropagation();
if (event["isScroll"] != true && !_canvas['userTyping']) {
event.preventDefault();
}
}
/**
* 增加鼠标事件
*/
function addMouseListener() {
_canvas.addEventListener("mousedown", onTouchBegin);
_canvas.addEventListener("mousemove", onMouseMove);
_canvas.addEventListener("mouseup", onTouchEnd);
}
/**
* 增加触摸事件
*/
function addTouchListener() {
_canvas.addEventListener("touchstart", (event) => {
let l = event.changedTouches.length;
for (let i = 0; i < l; i++) {
onTouchBegin(event.changedTouches[i]);
}
prevent(event);
}, false);
_canvas.addEventListener("touchmove", (event) => {
let l = event.changedTouches.length;
for (let i = 0; i < l; i++) {
onTouchMove(event.changedTouches[i]);
}
prevent(event);
}, false);
_canvas.addEventListener("touchend", (event) => {
let l = event.changedTouches.length;
for (let i = 0; i < l; i++) {
onTouchEnd(event.changedTouches[i]);
}
prevent(event);
}, false);
_canvas.addEventListener("touchcancel", (event) => {
let l = event.changedTouches.length;
for (let i = 0; i < l; i++) {
onTouchEnd(event.changedTouches[i]);
}
prevent(event);
}, false);
}
function onTouchBegin(event) {
let location = getLocation(event);
_touchHandler.onTouchBegin(location);
}
function onMouseMove(event) {
if (event.buttons === 0) {
onTouchEnd(event);
} else {
onTouchMove(event);
}
}
function onTouchMove(event) {
let location = getLocation(event);
_touchHandler.onTouchMove(location);
}
function onTouchEnd(event) {
let location = getLocation(event);
_touchHandler.onTouchEnd(location);
}
/**
* 获取当前点
*/
function getLocation(event) {
let doc = document.documentElement;
let box = _canvas.getBoundingClientRect();
let left = box.left + window.pageXOffset - doc.clientLeft;
let top = box.top + window.pageYOffset - doc.clientTop;
let x = event.pageX - left, newX = x;
let y = event.pageY - top, newY = y;
if (_rotation === 90) {
newX = y;
newY = box.width - x;
}
else if (_rotation === -90) {
newX = box.height - y;
newY = x;
}
newX = newX / _scaleX;
newY = newY / _scaleY;
return {
x: Math.round(newX),
y: Math.round(newY),
identifier: event.identifier || 0,
};
}
\ No newline at end of file
/**
* Created by rockyl on 2018/11/5.
*
* 渲染上下文
*/
import {updateScaleMode} from "./InteractContext";
import Bounds from "../../support/Bounds";
let _canvas, context, width, height;
let scaleX, scaleY, rotation = 0;
let _designWidth, _designHeight, _scaleMode, _modifyCanvasSize;
let shortcutCanvas;
/**
* 缩放模式
*
* SHOW_ALL: 全可见
* FIXED_WIDTH: 宽度固定
* FIXED_HEIGHT: 高度固定
*/
export const ScaleMode = {
SHOW_ALL: 'showAll',
FIXED_WIDTH: 'fixedWidth',
FIXED_HEIGHT: 'fixedHeight',
};
/**
* 装配上下文
* @param options
*/
export function setupContext(options: any = {}) {
const {canvas, designWidth, designHeight, scaleMode = ScaleMode.SHOW_ALL, modifyCanvasSize = false} = options;
_designWidth = designWidth;
_designHeight = designHeight;
_scaleMode = scaleMode;
_modifyCanvasSize = modifyCanvasSize;
_canvas = canvas;
context = canvas.getContext('2d');
updateScaleModeSelf();
}
/**
* 清空渲染上下文
*/
export function clear() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, width, height);
}
/**
* 获取渲染上下文
*/
export function getContext() {
return context;
}
/**
* 获取舞台尺寸
*/
export function getStageSize() {
return {
width,
height,
}
}
/**
* 获取舞台缩放
*/
export function getStageScale() {
return {
x: scaleX,
y: scaleY,
}
}
/**
* 获取舞台中心
*/
export function getStageCenter() {
return {
x: width / 2,
y: height / 2,
}
}
/**
* 创建canvas
*/
export function createCanvas() {
return document.createElement('canvas');
}
interface ShortcutParams {
imgType: string;
zoomToDom?: boolean;
quality?: number;
bounds?: Bounds;
}
/**
* 截图
* @param type 目标格式 0:Image, 1:DataURL
* @param params
*/
export async function shortcut(type: number = 0, params: ShortcutParams) {
let targetImg;
if(params.bounds || params.zoomToDom){
const dataUrl = _canvas.toDataURL('image/png');
const img = await dataUrlToImage(dataUrl);
targetImg = await shortcutWithSize(img, type, params.imgType, params.quality, params.bounds, params.zoomToDom ? scaleX : 1);
}else{
targetImg = _canvas.toDataURL(params.imgType, params.quality);
}
return targetImg;
}
function dataUrlToImage(dataUrl) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function () {
resolve(image);
};
image.onerror = function (e) {
reject(e);
};
image.src = dataUrl;
})
}
async function shortcutWithSize(img, type, imgType, quality?, bounds?:Bounds, scale = 1) {
if (!shortcutCanvas) {
shortcutCanvas = createCanvas();
}
const sx = bounds ? bounds.x || 0 : 0;
const sy = bounds ? bounds.y || 0 : 0;
const sw = bounds ? (bounds.width ? Math.min(bounds.width, width) : width) : width;
const sh = bounds ? (bounds.height ? Math.min(bounds.height, height) : height) : height;
const dw = sw * scale;
const dh = sh * scale;
shortcutCanvas.width = dw;
shortcutCanvas.height = dh;
const context = shortcutCanvas.getContext('2d');
context.drawImage(img, sx, sy, sw, sh, 0, 0, dw, dh);
const dataUrl = shortcutCanvas.toDataURL('image/' + imgType, quality);
switch (type) {
case 0:
return await dataUrlToImage(dataUrl);
case 1:
return dataUrl;
}
}
/**
* 更新缩放模式
*/
function updateScaleModeSelf() {
let parent = _canvas.parentElement;
let containerWidth = parent.clientWidth;
let containerHeight = parent.clientHeight;
const designWidth = _designWidth || containerWidth;
const designHeight = _designHeight || containerHeight;
scaleX = containerWidth / designWidth;
scaleY = containerHeight / designHeight;
switch (_scaleMode) {
case ScaleMode.SHOW_ALL:
width = designWidth;
height = designHeight;
break;
case ScaleMode.FIXED_WIDTH:
width = designWidth;
if(_modifyCanvasSize){
height = designHeight;
}else{
height = containerHeight / scaleX;
}
scaleY = scaleX;
break;
case ScaleMode.FIXED_HEIGHT:
if(_modifyCanvasSize){
width = designWidth;
}else{
width = containerWidth / scaleY;
}
height = designHeight;
scaleX = scaleY;
break;
}
updateScaleMode(scaleX, scaleY, rotation);
let styleWidth = _modifyCanvasSize ? designWidth * scaleX : containerWidth;
let styleHeight = _modifyCanvasSize ? designHeight * scaleY : containerHeight;
_canvas.width = width;
_canvas.height = height;
_canvas.style.display = 'block';
_canvas.style.width = styleWidth + 'px';
_canvas.style.height = styleHeight + 'px';
}
/**
* Created by rockyl on 2018/11/5.
*/
export {ScillaComponent} from "./ScillaComponent";
export {Entity} from './Entity'
export {Scene} from './Scene'
export {ScillaEvent} from './ScillaEvent'
export {getContext, createCanvas, getStageSize, getStageScale, getStageCenter, shortcut, ScaleMode} from './context/RenderContext';
export * from './manager'
export {default as Texture, createTexture} from './Texture'
export * from './Sheet'
export * from './FrameAnimation'
\ No newline at end of file
/**
* Created by rockyl on 2018/11/23.
*/
import {Entity, traverse, traversePostorder} from "./Entity";
import {injectProp} from "../tools/utils";
import {setupContext as setupInteractContext} from "./context/InteractContext";
import {clear, ScaleMode, setupContext as setupRenderContext} from "./context/RenderContext";
import './requestAnimationFrame';
/**
* 默认配置
*/
let options: any = {
fps: 60,
designWidth: 750,
designHeight: 1334,
scaleMode: ScaleMode.FIXED_WIDTH,
};
let root: Entity;
let _flush = 0, _currentFlush = 0;
let tsStart, tsLast;
let lastFPS = 0;
/**
* 装配引擎
* @param _options
*/
export function setup(_options?) {
injectProp(options, _options);
const {canvas, designWidth, designHeight, scaleMode, modifyCanvasSize} = options;
let canvasElement = typeof canvas == 'object' ? canvas : document.getElementById(canvas);
setupInteractContext({
canvas: canvasElement,
touchHandler: {
onTouchBegin,
onTouchMove,
onTouchEnd,
}
});
setupRenderContext({
canvas: canvasElement,
designWidth,
designHeight,
scaleMode,
modifyCanvasSize,
});
root = new Entity('root');
root._restrict();
}
/**
* 开始引擎
*/
export function start() {
root.enabled = true;
tsStart = Date.now();
startTick();
}
/**
* 暂停引擎
*/
export function pause() {
root.enabled = false;
stopTick();
}
/**
* 获取根Entity
*/
export function getRoot(): Entity {
return root;
}
/**
* 获取当前帧率
*/
export function getFPS(){
return lastFPS;
}
/**
* 开始时钟
*/
function startTick() {
_flush = 60 / options.fps - 1 >> 0;
if (_flush < 0) {
_flush = 0;
}
requestAnimationFrame(flush);
}
/**
* 停止时钟
*/
function stopTick() {
}
let tsLast2;
/**
* 时钟触发
*/
function flush(tsNow): void {
if (_flush == 0) {
onFrameTick(tsNow);
} else {
if (_currentFlush == 0) {
onFrameTick(tsNow);
_currentFlush = _flush;
} else {
_currentFlush--;
}
}
requestAnimationFrame(flush);
}
function onFrameTick(tsNow){
clear();
const tsNow2 = Date.now();
lastFPS = Math.floor(1000 / (tsNow - tsLast));
tsLast = tsNow;
tsLast2 = tsNow2;
const ts = tsNow - tsStart;
traverse(root, function (child) {
if(!child.isFree && child.enabled){
child.onUpdate(ts);
}else{
return true;
}
}, -1, true, function(current){
current.afterUpdate();
});
//const tsPass = Date.now() - tsNow;
}
/**
* 代理出来的onTouchBegin方法
* @param event
*/
function onTouchBegin(event) {
traversePostorder(root, function (child) {
return child.onInteract(0, event);
})
}
/**
* 代理出来的onTouchMove方法
* @param event
*/
function onTouchMove(event) {
traversePostorder(root, function (child) {
return child.onInteract(1, event);
})
}
/**
* 代理出来的onTouchEnd方法
* @param event
*/
function onTouchEnd(event) {
traversePostorder(root, function (child) {
return child.onInteract(2, event);
})
}
\ No newline at end of file
/**
* Created by Administrator on 2017/7/12.
*/
var lastTime = 0;
var vendors = ['webkit', 'moz'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || // name has changed in Webkit
window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = function(callback) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
/**
* Created by rockyl on 2018-12-04.
*/
import {destroyScene, setupScene, } from "./interpreter";
import {addLoader, cacheRes, destroyRes, getAllResUuids, loadJson5} from "../assets-manager";
import {getRoot, pause, Scene, start} from "../core";
export * from './interpreter'
let currentScene: Scene;
let resUUIDs;
/**
* 启动场景
* @param name
* @param progress
*/
export async function launchScene(name, progress?) {
const scene = await loadScene(`scenes/${name}.scene`, 'scene_' + name);
resUUIDs = getAllResUuids();
await scene.loadResGroup('preload', progress);
if(currentScene){
unmountScene(currentScene);
}
currentScene = scene;
mountScene(scene);
scene.loadResGroup('delay', progress);
}
/**
* 装载场景
* @param scene
*/
export function mountScene(scene){
pause();
setupScene(scene, getRoot());
start();
}
/**
* 卸载场景
* @param scene
*/
export function unmountScene(scene){
pause();
destroyScene(scene);
destroyRes(resUUIDs);
}
/**
* 加载场景资源
* @param url url
* @param uuid 唯一名
* @param cache 是否缓存
* @param config
* @return Promise<any> 资源
*/
async function loadScene(url, uuid?, cache = false, config?) {
const sceneConfig = await loadJson5(url);
const scene = new Scene();
scene.initByConfig(sceneConfig);
return scene;
}
/**
* 加载预制资源
* @param url url
* @param uuid 唯一名
* @param cache 是否缓存
* @param config
* @return Promise<any> 资源
*/
export async function loadPrefab(url, uuid?, cache = true, config?) {
let data = await loadJson5(url, uuid, false);
cacheRes(data, url, uuid);
return data;
}
addLoader('.pfb', loadPrefab);
/**
* Created by rockyl on 2018-12-03.
*
* 配置文件解释器
*/
import {Entity, Scene, ScillaEvent} from "../core";
import {getRes} from "../assets-manager";
let entityCache = {};
let entityCacheConfig;
const defMap = {};
let prefabID: number = 0;
export function registerDef(name, def) {
defMap[name] = def;
def.__class__ = name;
}
function getEntityCacheConfig(config) {
return config['entity-cache'] ? config['entity-cache'].concat() : [];
}
/**
* 装配场景
* @param scene
* @param root
*/
export function setupScene(scene: Scene, root: Entity): Scene {
scene.root = root;
const config = scene.config;
entityCacheConfig = getEntityCacheConfig(config);
instantiateConfig(config.root, root);
entityCache = {};
return scene;
}
/**
* 销毁场景
* @param scene
*/
export function destroyScene(scene: Scene) {
const root = scene.root;
root.removeAllComponents();
root.removeChildren();
}
/**
* 装配预制体
* @param config
*/
export function instantiate(config: any): Entity {
let pid = ++prefabID;
entityCacheConfig = getEntityCacheConfig(config);
for (let i = 0, li = entityCacheConfig.length; i < li; i++) {
entityCacheConfig[i] = pid + '_' + entityCacheConfig[i];
}
let rootConfig = config.root;
const entity = setupEntity(rootConfig, null, pid);
setupComponent(rootConfig, entity, true);
injectComponent(rootConfig, entity, true, pid);
entityCache = {};
return entity.children[0];
}
/**
* 装配树节点
* @param config
* @param root
*/
function instantiateConfig(config, root?: Entity): Entity {
const entity = setupEntity(config, root);
setupComponent(config, entity, true);
injectComponent(config, entity, true);
return entity;
}
/**
* 装配实体
* @param config
* @param root
* @param pid
*/
function setupEntity(config, root?: Entity, pid?): Entity {
let entity: Entity = null;
if (config) {
let {name, uuid, children} = config;
if (pid !== undefined && uuid !== undefined) {
uuid = pid + '_' + uuid;
}
entity = root || new Entity(name, uuid);
if (entityCacheConfig.indexOf(uuid) >= 0) {
entityCache[uuid] = entity;
}
if (children) {
for (let i = 0, li = children.length; i < li; i++) {
let child = children[i];
const childEntity = setupEntity(child, null, pid);
entity.addChild(childEntity);
}
}
if (!root) {
entity.enabled = !config.disabled;
}
}
return entity;
}
/**
* 装配组件
* @param config
* @param root
* @param includeSelf
*/
function setupComponent(config, root: Entity, includeSelf = false) {
if (includeSelf) {
instantiateComponents(root, config);
}
if (config && config.children) {
for (let i = 0, li = root.children.length; i < li; i++) {
const child = config.children[i];
const entity = root.children[i];
instantiateComponents(entity, child);
setupComponent(child, entity);
}
}
}
/**
* 注入组件参数
* @param config
* @param root
* @param includeSelf
* @param pid
*/
function injectComponent(config, root: Entity, includeSelf = false, pid?) {
if (includeSelf) {
injectComponents(root, config, pid);
}
if (config && config.children) {
for (let i = 0, li = root.children.length; i < li; i++) {
const child = config.children[i];
const entity = root.children[i];
injectComponents(entity, child, pid);
injectComponent(child, entity, false, pid);
}
}
}
/**
* 实例化组件列表
* @param entity
* @param config
*/
function instantiateComponents(entity: Entity, config: any) {
if (config.components) {
for (const component of config.components) {
instantiateComponent(entity, component);
}
}
}
/**
* 注入组件列表参数
* @param entity
* @param config
* @param pid
*/
function injectComponents(entity: Entity, config: any, pid?) {
if (config.components) {
const components = entity.components;
for (let i = 0, li = config.components.length; i < li; i++) {
const component = config.components[i];
const {properties} = component;
if (properties) {
injectProperties(components[i], properties, pid);
}
}
}
}
/**
* 实例化组件
* @param entity
* @param config
*/
function instantiateComponent(entity: Entity, config: any) {
const {script, properties} = config;
let def = getDefByName(script);
if (!def) {
return;
}
const instance: any = new def();
instance.enabled = !config.disabled;
entity.addComponent(instance);
}
/**
* 根据名称获取定义
* @param name
*/
function getDefByName(name): any {
let def;
/*if (name.indexOf('/') >= 0) {//addition
name = name.substr(name.lastIndexOf('/') + 1);
}*/
def = defMap[name];
if (!def) {
console.warn('missing def:', name);
return;
}
return def;
}
const skipKeys = ['_type_', '_constructor_'];
/**
* 属性注入
* @param node
* @param propertiesConfig
* @param pid
*/
function injectProperties(node, propertiesConfig, pid?) {
if (!node) {
console.warn('node is null.');
return;
}
for (const key in propertiesConfig) {
if (skipKeys.indexOf(key) >= 0) {
continue;
}
const propertyOfConfig: any = propertiesConfig[key];
let propertyOfInstance = node[key];
if (typeof propertyOfConfig === 'object') {
if (propertyOfInstance instanceof ScillaEvent) {
injectEvent(propertyOfInstance, propertyOfConfig, pid);
} else if (propertyOfConfig._type_ === 'raw') {
node[key] = propertyOfInstance = propertyOfConfig.data;
} else {
if (Array.isArray(propertyOfConfig) && !propertyOfInstance) {
node[key] = propertyOfInstance = []
}
node[key] = injectObject(propertyOfInstance, propertyOfConfig, pid);
}
} else {
injectBaseType(node, key, propertyOfConfig, pid);
}
}
}
function injectObject(propertyOfInstance, propertyOfConfig, pid?) {
if (propertyOfInstance === undefined) {
if (propertyOfConfig._type_) {
let def = getDefByName(propertyOfConfig._type_);
if (def) {
let constructorArgs = propertyOfConfig._constructor_;
if (constructorArgs && constructorArgs.length > 0) {
propertyOfInstance = def.constructor.apply(null, constructorArgs);
} else {
propertyOfInstance = new def();
}
}
}
}
if (propertyOfInstance) {
injectProperties(propertyOfInstance, propertyOfConfig, pid);
}
return propertyOfInstance;
}
function injectBaseType(node, key, propertyOfConfig, pid?) {
let propertyValue;
if (typeof propertyOfConfig === 'string') {
propertyValue = getLink(propertyOfConfig, pid);
} else {
propertyValue = propertyOfConfig;
}
node[key] = propertyValue;
}
function injectEvent(event: ScillaEvent, config, pid?) {
for (const {entity: entityName, component: componentIndex, method: methodName, param} of config) {
if (entityName && componentIndex >= 0 && methodName) {
const entity = getLink(entityName, pid);
const component = entity.components[componentIndex];
const method = component[methodName];
if (method) {
if (param == undefined) {
event.addListener(method, component, 0);
} else {
event.addListener(method, component, 0, param);
}
}
}
}
}
function getLink(str: string, pid?) {
let result;
if (str.indexOf('res|') == 0) { //res uuid
const uuid = str.substr(4);
result = getRes(uuid);
} else if (str.indexOf('entity|') == 0) { //entity uuid
const uuid = transPrefabUUID(str.substr(7), pid);
result = entityCache[uuid];
} else {
result = str;
}
return result;
}
function transPrefabUUID(uuid, pid) {
return pid ? pid + '_' + uuid : uuid;
}
/**
* Created by rockyl on 2018-12-05.
*/
import {injectProp} from "./tools/utils";
export const EngineConfig = {
lineHeightRatio: 1.2,
entityEnabled: true,
componentEnabled: true,
awakeComponentWhenAdded: true,
sleepComponentWhenRemoved: true,
drawRenderRect: false,
imgCrossOrigin: true,
};
export function modifyEngineConfig(_options) {
injectProp(EngineConfig, _options);
}
This diff is collapsed.
export { ShadowFilter } from './Filters';
export { ColorFilter } from './Filters';
export { ColorMatrixFilter } from './Filters';
export { BlurFilter } from './Filters';
\ No newline at end of file
/**
* Created by rockyl on 2018/11/15.
*/
export * from './core'
export * from './editor'
export * from './assets-manager'
export * from './support'
export * from './tools'
export * from './filter'
export * from './engine-config'
export * from './ReType'
\ No newline at end of file
/**
* Created by rockyl on 2018/11/7.
*
*/
/**
* 边界类
*/
export default class Bounds {
x;
y;
width;
height;
constructor(x = 0, y = 0, width = 0, height = 0) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
get left() {
return this.x;
}
set left(v) {
this.x = v;
}
get top() {
return this.y;
}
set top(v) {
this.y = v;
}
get right() {
return this.x + this.width;
}
set right(v) {
this.width = v - this.x;
}
get bottom() {
return this.y + this.height;
}
set bottom(v) {
this.height = v - this.y;
}
contains(x, y) {
return this.x <= x &&
this.x + this.width >= x &&
this.y <= y &&
this.y + this.height >= y;
}
setTo(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
copyFrom(target) {
this.x = target.x;
this.y = target.y;
this.width = target.width;
this.height = target.height;
}
clone() {
return new Bounds(this.x, this.y, this.width, this.height)
}
inflate(dx, dy) {
this.x -= dx;
this.width += 2 * dx;
this.y -= dy;
this.height += 2 * dy;
}
isEmpty() {
return this.width <= 0 || this.height <= 0;
}
setEmpty() {
this.x = 0;
this.y = 0;
this.width = 0;
this.height = 0;
}
intersects(toIntersect) {
return Math.max(this.x, toIntersect.x) <= Math.min(this.right, toIntersect.right)
&& Math.max(this.y, toIntersect.y) <= Math.min(this.bottom, toIntersect.bottom);
}
containsBounds(bounds) {
let r1 = bounds.x + bounds.width;
let b1 = bounds.y + bounds.height;
let r2 = this.x + this.width;
let b2 = this.y + this.height;
return (bounds.x >= this.x) && (bounds.x < r2) && (bounds.y >= this.y) && (bounds.y < b2) && (r1 > this.x) && (r1 <= r2) && (b1 > this.y) && (b1 <= b2);
}
equals(toCompare) {
if (this === toCompare) {
return true;
}
return this.x === toCompare.x && this.y === toCompare.y
&& this.width === toCompare.width && this.height === toCompare.height;
}
toString() {
const {x, y, width, height} = this;
return "(x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ")";
}
}
/**
* Created by rockyl on 2018-12-07.
*/
import {dirtyFieldTrigger} from "../tools/decorators";
const hsv2hsl = function(hue, sat, val) {
return [
hue,
(sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
hue / 2
];
};
// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
const isOnePointZero = function(n) {
return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
};
const isPercentage = function(n) {
return typeof n === 'string' && n.indexOf('%') !== -1;
};
// Take input from [0, n] and return it as [0, 1]
const bound01 = function(value, max) {
if (isOnePointZero(value)) value = '100%';
const processPercent = isPercentage(value);
value = Math.min(max, Math.max(0, parseFloat(value)));
// Automatically convert percentage into number
if (processPercent) {
value = Math.floor(value * max) / 100;
}
// Handle floating point rounding errors
if ((Math.abs(value - max) < 0.000001)) {
return 1;
}
// Convert into [0, 1] range if it isn't already
return (value % max) / parseFloat(max);
};
const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };
const toHex = function({ r, g, b }) {
const hexOne = function(value) {
value = Math.min(Math.round(value), 255);
const high = Math.floor(value / 16);
const low = value % 16;
return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
};
if (isNaN(r) || isNaN(g) || isNaN(b)) return '';
return '#' + hexOne(r) + hexOne(g) + hexOne(b);
};
const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };
const parseHexChannel = function(hex) {
if (hex.length === 2) {
return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
}
return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
};
const hsl2hsv = function(hue, sat, light) {
sat = sat / 100;
light = light / 100;
let smin = sat;
const lmin = Math.max(light, 0.01);
let sv;
let v;
light *= 2;
sat *= (light <= 1) ? light : 2 - light;
smin *= lmin <= 1 ? lmin : 2 - lmin;
v = (light + sat) / 2;
sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);
return {
h: hue,
s: sv * 100,
v: v * 100
};
};
// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
const rgb2hsv = function(r, g, b) {
r = bound01(r, 255);
g = bound01(g, 255);
b = bound01(b, 255);
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s;
let v = max;
const d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0; // achromatic
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return { h: h * 360, s: s * 100, v: v * 100 };
};
// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
const hsv2rgb = function(h, s, v) {
h = bound01(h, 360) * 6;
s = bound01(s, 100);
v = bound01(v, 100);
const i = Math.floor(h);
const f = h - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
const mod = i % 6;
const r = [v, q, p, p, t, v][mod];
const g = [t, v, v, q, p, p][mod];
const b = [p, p, t, v, v, q][mod];
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
};
/**
* 颜色
*/
export default class Color {
enableAlpha;
format;
private _hue;
private _saturation;
private _value;
private _valueStr;
private _alpha;
constructor(value?) {
this._hue = 0;
this._saturation = 100;
this._value = 100;
this._alpha = 100;
this.enableAlpha = true;
this.format = 'hex';
if(value){
this.value = value;
}else{
this.value = '';
}
this.doOnChange();
}
get value(){
return this._valueStr;
}
set value(v){
this.fromString(v);
}
set(prop, value) {
if (arguments.length === 1 && typeof prop === 'object') {
for (let p in prop) {
if (prop.hasOwnProperty(p)) {
this.set(p, prop[p]);
}
}
return;
}
this['_' + prop] = value;
this.doOnChange();
}
get(prop) {
return this['_' + prop];
}
toRgb() {
return hsv2rgb(this._hue, this._saturation, this._value);
}
fromString(value) {
if (!value) {
this._hue = 0;
this._saturation = 100;
this._value = 100;
this.doOnChange();
return;
}
const fromHSV = (h, s, v) => {
this._hue = Math.max(0, Math.min(360, h));
this._saturation = Math.max(0, Math.min(100, s));
this._value = Math.max(0, Math.min(100, v));
this.doOnChange();
};
if (value.indexOf('hsl') !== -1) {
const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v);
}
} else if (value.indexOf('hsv') !== -1) {
const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
fromHSV(parts[0], parts[1], parts[2]);
}
} else if (value.indexOf('rgb') !== -1) {
const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v);
}
} else if (value.indexOf('#') !== -1) {
const hex = value.replace('#', '').trim();
let r, g, b;
if (hex.length === 3) {
r = parseHexChannel(hex[0] + hex[0]);
g = parseHexChannel(hex[1] + hex[1]);
b = parseHexChannel(hex[2] + hex[2]);
} else if (hex.length === 6 || hex.length === 8) {
r = parseHexChannel(hex.substring(0, 2));
g = parseHexChannel(hex.substring(2, 4));
b = parseHexChannel(hex.substring(4, 6));
}
if (hex.length === 8) {
this._alpha = Math.floor(parseHexChannel(hex.substring(6)) / 255 * 100);
} else if (hex.length === 3 || hex.length === 6) {
this._alpha = 100;
}
const { h, s, v } = rgb2hsv(r, g, b);
fromHSV(h, s, v);
}
}
compare(color) {
return Math.abs(color._hue - this._hue) < 2 &&
Math.abs(color._saturation - this._saturation) < 1 &&
Math.abs(color._value - this._value) < 1 &&
Math.abs(color._alpha - this._alpha) < 1;
}
doOnChange() {
const { _hue, _saturation, _value, _alpha, format } = this;
if (this.enableAlpha) {
switch (format) {
case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this._valueStr = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
break;
case 'hsv':
this._valueStr = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
break;
default:
const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
this._valueStr = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
}
} else {
switch (format) {
case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this._valueStr = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
break;
case 'hsv':
this._valueStr = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
break;
case 'rgb':
const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
this._valueStr = `rgb(${r}, ${g}, ${b})`;
break;
default:
this._valueStr = toHex(hsv2rgb(_hue, _saturation, _value));
}
}
}
};
\ No newline at end of file
/**
* Created by rockyl on 2018-11-25.
*/
'use strict';
var has = Object.prototype.hasOwnProperty
, prefix = '~';
/**
* Constructor to create a storage for our `EE` objects.
* An `Events` instance is a plain object whose properties are event names.
*
* @constructor
* @private
*/
function Events() {}
//
// We try to not inherit from `Object.prototype`. In some engines creating an
// instance in this way is faster than calling `Object.create(null)` directly.
// If `Object.create(null)` is not supported we prefix the event names with a
// character to make sure that the built-in object properties are not
// overridden or used as an attack vector.
//
if (Object.create) {
Events.prototype = Object.create(null);
//
// This hack is needed because the `__proto__` property is still inherited in
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
//
if (!new Events().__proto__) prefix = '';
}
/**
* Representation of a single event listener.
*
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
* @constructor
* @private
*/
function EE(fn, context, once) {
this.fn = fn;
this.context = context;
this.once = once || false;
}
/**
* Add a listener for a given event.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} context The context to invoke the listener with.
* @param {Boolean} once Specify if the listener is a one-time listener.
* @returns {EventEmitter}
* @private
*/
function addListener(emitter, event, fn, context, once) {
if (typeof fn !== 'function') {
throw new TypeError('The listener must be a function');
}
var listener = new EE(fn, context || emitter, once)
, evt = prefix ? prefix + event : event;
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
else emitter._events[evt] = [emitter._events[evt], listener];
return emitter;
}
/**
* Clear event by name.
*
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
* @param {(String|Symbol)} evt The Event name.
* @private
*/
function clearEvent(emitter, evt) {
if (--emitter._eventsCount === 0) emitter._events = new Events();
else delete emitter._events[evt];
}
/**
* Minimal `EventEmitter` interface that is molded against the Node.js
* `EventEmitter` interface.
*
* @constructor
* @public
*/
export default class EventEmitter{
_events;
_eventsCount;
off;
addListener;
static get prefixed(){
return prefix;
}
constructor() {
this._events = new Events();
this._eventsCount = 0;
this.off = this.removeListener;
this.addListener = this.on;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
eventNames() {
var names = []
, events
, name;
if (this._eventsCount === 0) return names;
for (name in (events = this._events)) {
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
}
//if (Object.getOwnPropertySymbols) {
// return names.concat(Object.getOwnPropertySymbols(events));
//}
return names;
}
/**
* Return the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Array} The registered listeners.
* @public
*/
listeners(event) {
var evt = prefix ? prefix + event : event
, handlers = this._events[evt];
if (!handlers) return [];
if (handlers.fn) return [handlers.fn];
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
ee[i] = handlers[i].fn;
}
return ee;
}
/**
* Return the number of listeners listening to a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Number} The number of listeners.
* @public
*/
listenerCount(event) {
var evt = prefix ? prefix + event : event
, listeners = this._events[evt];
if (!listeners) return 0;
if (listeners.fn) return 1;
return listeners.length;
}
/**
* Calls each of the listeners registered for a given event.
*
* @param {(String|Symbol)} event The event name.
* @returns {Boolean} `true` if the event had listeners, else `false`.
* @public
*/
emit(event, a1?, a2?, a3?, a4?, a5?) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return false;
var listeners = this._events[evt]
, len = arguments.length
, args
, i;
if (listeners.fn) {
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
switch (len) {
case 1: return listeners.fn.call(listeners.context), true;
case 2: return listeners.fn.call(listeners.context, a1), true;
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
}
for (i = 1, args = new Array(len -1); i < len; i++) {
args[i - 1] = arguments[i];
}
listeners.fn.apply(listeners.context, args);
} else {
var length = listeners.length
, j;
for (i = 0; i < length; i++) {
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
switch (len) {
case 1: listeners[i].fn.call(listeners[i].context); break;
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
default:
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
args[j - 1] = arguments[j];
}
listeners[i].fn.apply(listeners[i].context, args);
}
}
}
return true;
}
/**
* Add a listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
on(event, fn, context) {
return addListener(this, event, fn, context, false);
};
/**
* Add a one-time listener for a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn The listener function.
* @param {*} [context=this] The context to invoke the listener with.
* @returns {EventEmitter} `this`.
* @public
*/
once(event, fn, context) {
return addListener(this, event, fn, context, true);
}
/**
* Remove the listeners of a given event.
*
* @param {(String|Symbol)} event The event name.
* @param {Function} fn Only remove the listeners that match this function.
* @param {*} context Only remove the listeners that have this context.
* @param {Boolean} once Only remove one-time listeners.
* @returns {EventEmitter} `this`.
* @public
*/
removeListener(event, fn, context, once) {
var evt = prefix ? prefix + event : event;
if (!this._events[evt]) return this;
if (!fn) {
clearEvent(this, evt);
return this;
}
var listeners = this._events[evt];
if (listeners.fn) {
if (
listeners.fn === fn &&
(!once || listeners.once) &&
(!context || listeners.context === context)
) {
clearEvent(this, evt);
}
} else {
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
if (
listeners[i].fn !== fn ||
(once && !listeners[i].once) ||
(context && listeners[i].context !== context)
) {
events.push(listeners[i]);
}
}
//
// Reset the array, or remove it completely if we have no more listeners.
//
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
else clearEvent(this, evt);
}
return this;
}
/**
* Remove all listeners, or those of the specified event.
*
* @param {(String|Symbol)} [event] The event name.
* @returns {EventEmitter} `this`.
* @public
*/
removeAllListeners(event) {
var evt;
if (event) {
evt = prefix ? prefix + event : event;
if (this._events[evt]) clearEvent(this, evt);
} else {
this._events = new Events();
this._eventsCount = 0;
}
return this;
}
}
\ No newline at end of file
/**
* Created by rockyl on 2019-01-04.
*/
export default class LocalStorage {
ID: string;
constructor(ID: string) {
this.ID = ID;
}
getName(key: string, prefix: string = null): string {
return (prefix || !this.ID || this.ID == '' ? prefix : this.ID) + '_' + key;
}
getItem(key: string, prefix: string = null): string {
return localStorage.getItem(this.getName(key, prefix));
}
setItem(key: string, value: string, prefix: string = null) {
localStorage.setItem(this.getName(key, prefix), value);
}
getItemObj(key: string, defaultObj: any = null, prefix: string = null): any {
let result: any;
try {
result = JSON.parse(this.getItem(key, prefix));
} catch (e) {
}
if (!result) {
result = defaultObj;
}
return result;
}
setItemObj(key: string, itemObj: any, prefix: string = null) {
this.setItem(key, JSON.stringify(itemObj), prefix);
}
}
\ No newline at end of file
This diff is collapsed.
/**
* Created by rockyl on 2018/8/1.
*
* 对象池
*/
let all = {};
function getGroup(name){
let group = all[name];
if(!group){
throw new Error('group ' + name + ' not registered.');
}
return group;
}
export function register(name, newFunc, initFunc){
all[name] = {name, newFunc, initFunc, pool: []};
}
export function get(name, ...params){
let group = getGroup(name);
let {newFunc, initFunc, pool} = group;
let instance;
if(pool.length == 0){
instance = newFunc();
}else{
instance = pool.pop();
}
initFunc(instance, ...params);
return instance;
}
export function recycle(name, instance){
let group = getGroup(name);
group.pool.push(instance);
}
\ No newline at end of file
/**
* Created by rockyl on 2018-12-07.
*/
import {dirtyFieldTrigger} from "../tools/decorators";
/**
* 尺寸
*/
export default class Size {
@dirtyFieldTrigger
width: number;
@dirtyFieldTrigger
height: number;
onChange;
constructor(width = NaN, height = NaN) {
this.width = width;
this.height = height;
}
setNaN(){
this.width = NaN;
this.height = NaN;
}
isEmpty(){
}
set(width?, height?) {
if (width !== undefined) {
this.width = width;
}
if (height !== undefined) {
this.height = height;
}
}
clone() {
return new Size(this.width, this.height);
}
copyFrom(target) {
this.width = target.width;
this.height = target.height;
}
onModify(value, key, oldValue) {
this.onChange && this.onChange(value, key, oldValue);
}
}
/**
* Created by rockyl on 2018-11-29.
*/
import {dirtyFieldTrigger} from "../tools/decorators";
export enum FontStyle{
/**
* 正常
*/
NORMAL = 'normal',
/**
* 斜体
*/
ITALIC = 'italic',
/**
* 倾斜
*/
OBLIQUE = 'oblique',
}
export enum FontVariant{
/**
* 正常
*/
NORMAL = 'normal',
/**
* 小型大写
*/
SMALL_CAPS = 'small-caps',
}
export enum FontWeight{
/**
* 正常
*/
NORMAL = 'normal',
/**
* 粗体
*/
BOLD = 'bold',
/**
* 更粗
*/
BOLDER = 'bolder',
/**
* 更细
*/
LIGHTER = 'lighter',
}
/**
* 文本样式
*/
export class TextStyle {
private readonly _callback;
onChange;
/**
* 字体样式
*/
@dirtyFieldTrigger
fontStyle: FontStyle = FontStyle.NORMAL;
/**
* 字体倾斜
*/
@dirtyFieldTrigger
fontVariant: FontVariant = FontVariant.NORMAL;
/**
* 字体宽度
*/
@dirtyFieldTrigger
fontWeight: FontWeight = FontWeight.NORMAL;
/**
* 字体尺寸
*/
@dirtyFieldTrigger
fontSize: number = 25;
/**
* 字体名称
*/
@dirtyFieldTrigger
fontFamily: string = 'Arial';
onModify(value, key, oldValue) {
this.onChange && this.onChange(value, key, oldValue, 'textStyle');
}
}
This diff is collapsed.
This diff is collapsed.
/**
* Created by rockyl on 2018/11/15.
*
* 支撑类库
*/
export {default as Bounds} from './Bounds'
export {default as Vector2D, createVector2D, releaseVector2D} from './Vector2D'
export {createTween, Tween} from './Tween'
export {default as Matrix} from './Matrix'
export {default as Size} from './Size'
//export {default as Color} from './Color'
export {default as LocalStorage} from './LocalStorage'
export {TextStyle} from './TextStyle'
export {default as EventEmitter} from './EventEmitter';
\ No newline at end of file
/**
* Created by rockyl on 2018/11/9.
*
* 装饰器
*/
/**
* 属性修改时触发
* @param onChange
*/
export function fieldChanged(onChange) {
return function (target: any, key: string) {
const privateKey = '_' + key;
Object.defineProperty(target, key, {
enumerable: true,
get: function () {
return this[privateKey];
},
set: function (v) {
const oldValue = this[privateKey];
if (oldValue !== v) {
this[privateKey] = v;
onChange.apply(this, [v, key, oldValue]);
}
}
})
}
}
/**
* 属性变脏时设置宿主的dirty属性为true
*/
export const dirtyFieldDetector = fieldChanged(
function (value, key, oldValue) {
this['dirty'] = true;
}
);
/**
* 属性变脏时触发onModify方法
*/
export const dirtyFieldTrigger = fieldChanged(
function (value, key, oldValue) {
this['onModify'] && this['onModify'](value, key, oldValue);
}
);
This diff is collapsed.
/**
* Created by rockyl on 2018/11/16.
*/
import * as decorators from './decorators'
import * as ease from './ease'
import * as math from './math'
import * as utils from './utils'
import * as timeUtils from './time'
import {Ease} from "./ease";
export {
decorators,
ease,
Ease,
math,
utils,
timeUtils,
}
/**
* Created by rockyl on 2018/11/8.
*
* 数学工具
*/
/**
* 线性插值
* @param begin number
* @param end number
* @param t number
* @param allowOutOfBounds
* @return number
*/
export function lerp(begin, end, t, allowOutOfBounds = false) {
const type = typeof begin;
if (type !== typeof end) {
console.error('begin and end need same type')
}
if (!allowOutOfBounds) {
t = Math.max(0, Math.min(1, t));
}
let sign = end - begin;
sign = sign > 0 ? 1 : (sign < 0 ? -1 : 0);
const distance = Math.abs(end - begin);
return begin + distance * t * sign;
}
/**
* 线性插值对象
* @param begin
* @param end
* @param t
* @param clazz
* @param fields
* @param allowOutOfBounds
* @return
*/
export function lerpObj(begin, end, t, clazz, fields, allowOutOfBounds = false) {
const type = typeof begin;
if (type !== typeof end) {
console.error('begin and end need same type')
}
const temp = new clazz();
for (let field of fields) {
temp[field] = lerp(begin[field], end[field], t, allowOutOfBounds);
}
return temp;
}
/**
* 随机生成一个整数
* @param max
* @param min
*/
export function makeRandomInt(max: number, min: number = 0): number {
return Math.floor(Math.random() * (max - min)) + min;
}
/**
* 打乱一个数组
* @param arr
* @returns {any}
*/
export function mixArray(arr: any): Array<any> {
for (let i: number = 0, len: number = Math.round(arr.length / 2); i < len; i++) {
let a: number = makeRandomInt(arr.length);
let b: number = makeRandomInt(arr.length);
let temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
return arr;
}
This diff is collapsed.
This diff is collapsed.
{
"compilerOptions": {
"target": "es5",
"experimentalDecorators": true,
"sourceMap": true,
"declarationDir": "./types",
"declaration": true,
"lib": [
"es5",
"es6",
"dom",
"es2015.promise"
]
},
"include": [
"src",
"libs"
]
}
\ No newline at end of file
This diff is collapsed.
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