Commit 553d9a06 authored by wangjianfeng.yz's avatar wangjianfeng.yz

srcNew

parent 959e376c
......@@ -5,5 +5,4 @@ node_modules
debug
.vscode
dist
docs
srcNew
\ No newline at end of file
docs
\ No newline at end of file
import { EventDispatcher, Event } from "./events";
import { clamp } from "./utils";
/**
* 就是个时间轴,setValue和resetValue方法自行实现
*/
export interface IAnimationTrack {
setValue: (time: number) => void
resetValue: () => void
}
/**
* 不需要挂到节点上
* 暂时没有帧,只有时间,以后再说
* 用update更新
* 通用的动画类,track自定义
*/
export class AnimationClip extends EventDispatcher {
name: string
/**
* 所有的动画数据
*/
protected tracks: IAnimationTrack[];
private _totalTime: number;
/**
* 动画总时长,一般是所有tracks里时间最长的那个
* @readonly
*/
get totalTime() {
return this._totalTime;
}
/**
*
* @param tracks
* @param totalTime 总时间自行传入,秒计,
*/
constructor(tracks: IAnimationTrack[], totalTime: number) {
super()
this._instanceType = "AnimationClip"
this.tracks = tracks;
this._totalTime = totalTime
}
/**
* 初始化方法
* @param tracks
* @param totalTime
*/
init(tracks: IAnimationTrack[], totalTime: number) {
this.tracks = tracks;
this._totalTime = totalTime;
//事件移除,其实帧数完全一致的话,不会出问题,一样能触发回调
if (this.startAniRangeFun) this.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this);
//下面这些暂时不考虑初始化,MovieClip暂时也没有,估计静止的时候都会出问题
// this._isPlaying = true;
// this._isFront = true;
// this.lastTime = null;
// this.curTime = 0;
// this._endMark = false;
}
private _isPlaying: boolean = true;
/**
* 是否播放中
* @readonly
*/
public get isPlaying(): boolean {
return this._isPlaying;
}
private _isFront: boolean = true;
/**
* 是否正向播放
* @readonly
*/
get isFront(): boolean {
return this._isFront;
}
/**
* 上个时间,用来确定是否更新
*/
protected lastTime: number = null;
/**
* 记录时间
*/
protected curTime: number = 0;
/**
* 当前时间
*/
get currentTime() {
return this.curTime;
}
private _endMark: boolean = false;
/**
* 需要挂在循环里的方法,传时间间隔
* @param time 一般为秒计
*/
update(time: number) {
if (!this.tracks || !this.tracks.length) return;
//时间不等,直接播放
if (this.curTime !== this.lastTime) {
this.rectify();
this._endMark = false;
return;
}
//时间没有,或没在播放
if (time <= 0 || !this._isPlaying) return;
this._endMark = false;
if (this._isFront) {
this.curTime += time;
if (this.curTime > this._totalTime) {
this.curTime = 0;
this._endMark = true;
}
} else {
this.curTime -= time;
if (this.curTime < 0) {
this.curTime = this._totalTime;
this._endMark = true;
}
}
if (this.curTime !== this.lastTime) {
//矫正
this.rectify();
//派发每帧事件
this.dispatchEvent(Event.ENTER_FRAME);
//派发结束帧事件
if (this._endMark && this.hasEventListener(Event.END_FRAME)) {
this.dispatchEvent(Event.END_FRAME);
}
}
}
/**
* 从当前时间点播放
* @param isFront 默认true正向
*/
play(isFront: boolean = true) {
this._isFront = isFront;
this._isPlaying = true;
}
/**
* 停在当前时间
*/
stop() {
this._isPlaying = false;
this.lastTime = this.curTime;
}
/**
* 从某时刻开始播放
* @param time
* @param isFront 默认true,正向播放
*/
public gotoAndPlay(time: number, isFront: boolean = true): void {
let s = this;
s._isFront = isFront;
s._isPlaying = true;
// if (time > s._totalTime) time = s._totalTime;
// if (time < 0) time = 0;
time = clamp(time, 0, s._totalTime);//改成用clamp
s.curTime = time;
}
/**
* 停在指定时间
* @param time
* @param force 是否强制更新,默认false,如果发现没stop在指定位置,可以试试设置true
*/
public gotoAndStop(time: number, force: boolean = false): void {
this._isPlaying = false;
// if (time > this.totalTime) time = this.totalTime;
// if (time < 0) time = 0;
time = clamp(time, 0, this._totalTime);//改成用clamp
this.curTime = time;
//这样会强制更新
if (force) this.lastTime = null
}
private startAniRangeFun;
/**
* 在一个区间范围内播放
* @param beginTime 开始时间,秒计,默认0,
* @param endTime 结束时间,秒计,默认_totalTime
* @param loops 循环次数,0或负数表示无限循环,默认1
* @param callback 播放完成后的回调,无限循环时无效
*/
public startAniRange(
beginTime: number = 0,
endTime: number = this._totalTime,
loops: number = 1,
callback?: () => void
) {
//loops处理下
loops = loops || 0;//去掉null等等
loops = Math.max(0, loops);//去掉负数
// if (beginTime <= 0) beginTime = 0;
// if (beginTime > this._totalTime) beginTime = this._totalTime;
// if (endTime <= 0) endTime = 0;
// if (endTime > this._totalTime) endTime = this._totalTime;
beginTime = clamp(beginTime, 0, this._totalTime);
endTime = clamp(endTime, 0, this._totalTime);
if (beginTime === endTime) {
this.gotoAndStop(beginTime)
//如果相等
return
} else {
this._isFront = beginTime < endTime;
}
//移除原先的绑定吧
if (this.startAniRangeFun) this.removeEventListener(Event.ENTER_FRAME, this.startAniRangeFun, this)
this.curTime = beginTime;
this._isPlaying = true;
let loopCount = loops ? (loops + 0.5 >> 0) : Infinity;
this.addEventListener(Event.ENTER_FRAME, this.startAniRangeFun = (e: Event) => {
let s: AnimationClip = e.target;
let cond = s._isFront ? s.curTime >= endTime : s.curTime <= endTime;
if (cond || s._endMark) {
loopCount--;
if (loopCount <= 0) {
s._isPlaying = false;
s.curTime = endTime;//这个在下一帧才生效,所以加一个矫正rectify
// s.rectify();
s.removeEventListener(Event.ENTER_FRAME, s.startAniRangeFun, s);
s.startAniRangeFun = null;
callback && callback();
s.rectify();//放在最后吧,callback里也可能干了啥
} else {
s.curTime = beginTime;
}
}
}, this)
}
/**
* 矫正
*/
protected rectify() {
if (!this.tracks || !this.tracks.length) return;
for (var i = 0; i < this.tracks.length; i++) {
this.tracks[i].setValue(this.curTime)
}
//设置相等
this.lastTime = this.curTime;
}
/**
* 用于重置初始状态,因为每个track的0状态也不一定是初始状态
* 所以各自track自行处理
*/
public resetState() {
this.stop();
for (var i = 0; i < this.tracks.length; i++) this.tracks[i].resetValue()
}
}
\ No newline at end of file
import { HashObject } from "./HashObject";
import { AnimationClip } from "./AnimationClip";
/**
* 用于管理传入的AnimationClips,为了多clips动画的切换
* 不需要加入到舞台中
* 自行加入到循环里,执行update
* 暂时不允许多状态存在
*/
export class AnimationManager extends HashObject {
protected currentClip: AnimationClip;
/**
* 传入
* @param animationClips 所有的clip
*/
constructor(
private animationClips: AnimationClip[]
) {
super()
this._instanceType = "AnimationManager";
//所有的都停下先
this.stop();
}
/**
* 获取一个AnimationClip,比如想自己控制
* @param name
*/
getAniClipByName(name: string) {
for (var i = 0; i < this.animationClips.length; i++) {
if (this.animationClips[i].name == name) return this.animationClips[i]
}
return null;
}
/**
* 切换动画,会停止当前播放的动画
* 精细播放自己获取并控制,这个方法只针对多clip切换
* @param name
* @param loops 循环次数,0表示无限循环,默认0
*/
showAni(name: string, loops: number = 0, callback?: () => void): AnimationClip {
var clip = this.getAniClipByName(name)
if (!clip) return null;
//因为正在播放的动画会影响其他动画的初始状态,必须先重置
if (this.currentClip) this.currentClip.resetState();
this.currentClip = clip;
//播放
clip.startAniRange(0, undefined, loops, callback)
//返回一个,可以自己玩
return clip
}
/**
* 停止
*/
stop() {
this.animationClips.forEach((a) => { a.stop(); })
this.currentClip = null;
}
/**
* 重置并停止
*/
reset() {
this.animationClips.forEach((a) => { a.resetState(); })
this.currentClip = null;
}
/**
* 循环更新方法
* @param deltaTime 按秒计,默认0.0167
*/
update(deltaTime: number = 0.0167) {
for (var i = 0; i < this.animationClips.length; i++) {
this.animationClips[i].update(deltaTime)
}
}
destroy() {
this.animationClips = null;
this.currentClip = null;
}
}
\ No newline at end of file
import { createCanvas } from "./utils";
import { Sprite } from "./display";
import { Texture } from "./texture";
export class FpsPanel extends Sprite {
private context: CanvasRenderingContext2D;
private bgColor: string = '#002002';
private textColor: string = '#0ff0ff';
private PR: number = 3;
private WIDTH = 80 * this.PR;
private HEIGHT = 48 * this.PR;
private TEXT_X = 3 * this.PR;
private TEXT_Y = 2 * this.PR;
private GRAPH_X = 3 * this.PR;
private GRAPH_Y = 15 * this.PR;
private GRAPH_WIDTH = 74 * this.PR;
private GRAPH_HEIGHT = 30 * this.PR;
private GRAPH_SIZE = 74;
private maxValue = 120;
private min = Infinity;
private max = 0;
private items = [];
/**
* 帧率面板
* 后续可以加入每次drawCall,总绘制对象等等
*/
constructor() {
super()
//离屏canvas
var canvas = createCanvas();
canvas.width = this.WIDTH;
canvas.height = this.HEIGHT;
this.texture = Texture.fromCanvas(canvas)
this.context = canvas.getContext("2d");
this.context.font = 'bold ' + (9 * this.PR) + 'px Helvetica,Arial,sans-serif';
this.context.textBaseline = 'top';
this.updateText("FPS");
}
private lastTime: number = Date.now();
private frames: number = 0;
reset() {
this.lastTime = Date.now();
this.min = Infinity;
this.max = 0;
this.items.length = 0;
this.frames = 0;
}
update() {
this.frames++;
var time = Date.now();
//每秒跑一次
if (time >= this.lastTime + 1000) {
var value = (this.frames * 1000) / (time - this.lastTime);
this.updatePanel(value)
this.lastTime = time;
this.frames = 0;
}
super.update();
}
private updatePanel(value: number) {
const { items, GRAPH_SIZE, context, GRAPH_X, textColor, GRAPH_Y, PR, GRAPH_HEIGHT, bgColor, maxValue } = this
items.push(value);
while (items.length > GRAPH_SIZE) {
items.shift();
}
this.min = Math.min(this.min, value);
this.max = Math.max(this.max, value);
this.updateText(Math.round(value) + ' FPS (' + Math.round(this.min) + '-' + Math.round(this.max) + ')');
for (var i = 0; i < items.length; i++) {
var startPos = GRAPH_X + (i + GRAPH_SIZE - items.length) * PR;
context.fillStyle = textColor;
context.globalAlpha = 1;
context.fillRect(startPos, GRAPH_Y, PR, GRAPH_HEIGHT);
context.fillStyle = bgColor;
context.globalAlpha = 0.9;
context.fillRect(startPos, GRAPH_Y, PR, Math.round((1 - (items[i] / maxValue)) * GRAPH_HEIGHT));
}
this.texture.update();
}
private updateText(text: string) {
const { context, bgColor, textColor, WIDTH, HEIGHT, TEXT_X, TEXT_Y, GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT } = this
context.fillStyle = bgColor;
context.globalAlpha = 1;
context.fillRect(0, 0, WIDTH, HEIGHT);
context.fillStyle = textColor;
context.fillText(text, TEXT_X, TEXT_Y);
context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT);
context.fillStyle = bgColor;
context.globalAlpha = 0.9;
context.fillRect(GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT);
}
}
/**
* 基础对象,用于标记类名及唯一实例id
* 只在引擎内部使用
*/
export abstract class HashObject {
protected _instanceId: number = 0;
protected _instanceType: string = "HashObject";
protected static _object_id = 0;
constructor() {
this._instanceId = HashObject._object_id++;
}
/**
* 实例唯一id
* @property instanceId
* @public
* @since 1.0.0
* @return {number}
* @readonly
*/
public get instanceId(): number {
return this._instanceId;
}
/**
* 实例类型
* @property instanceType
* @since 1.0.0
* @public
* @return {string}
* @readonly
*/
public get instanceType(): string {
return this._instanceType;
}
/**
* 抽象方法,销毁一个对象,子类写
* @method destroy
* @since 1.0.0
* @public
* @return {void}
*/
abstract destroy(): void;
}
//这里都是一些常量
/**
* 版本号
* @static
* @constant
* @name VERSION
* @type {string}
*/
export const VERSION = "2.0.67";
let PI: number = Math.PI;
let HalfPI: number = PI >> 1;
let PacPI: number = PI + HalfPI;
/**
* @method cos
* @private
* @param {number} angle,弧度制
* @return {number}
*/
export function cos(angle: number): number {
switch (angle) {
case HalfPI:
case -PacPI:
return 0;
case PI:
case -PI:
return -1;
case PacPI:
case -HalfPI:
return 0;
default:
return Math.cos(angle);
}
}
/**
* @method sin
* @param {number} angle,弧度制
* @return {number}
*/
export function sin(angle: number): number {
switch (angle) {
case HalfPI:
case -PacPI:
return 1;
case PI:
case -PI:
return 0;
case PacPI:
case -HalfPI:
return -1;
default:
return Math.sin(angle);
}
}
/**
* Two Pi.
*
* @static
* @constant
* @type {number}
*/
export const PI_2: number = PI * 2;
/**
* Conversion factor for converting radians to degrees.
* 弧度转角度
* @static
* @constant
* @type {number}
*/
export const RAD_TO_DEG: number = 180 / PI;
/**
* Conversion factor for converting degrees to radians.
* 角度转弧度
* @static
* @constant
* @type {number}
*/
export const DEG_TO_RAD: number = PI / 180;
/**
* 渲染方式记录
* @static
* @constant
* @name RENDERER_TYPE
* @type {object}
* @property {number} UNKNOWN - Unknown render type.
* @property {number} WEBGL - WebGL render type.
* @property {number} CANVAS - Canvas render type.
*/
export enum RENDERER_TYPE {
UNKNOWN = 0,
WEBGL,
CANVAS,
};
/**
* 各种形状
* @static
* @constant
* @name SHAPES
* @type {object}
* @property {number} POLY Polygon 多边形
* @property {number} RECT Rectangle 矩形
* @property {number} CIRC Circle 圆形
* @property {number} ELIP Ellipse 椭圆
* @property {number} RREC Rounded Rectangle 圆角矩形
*/
export enum SHAPES {
POLY = 0,
RECT,
CIRC,
ELIP,
RREC,
};
/**
* 着色器浮点精度
* @static
* @constant
* @name PRECISION
* @type {object}
* @property {string} LOW='lowp'
* @property {string} MEDIUM='mediump'
* @property {string} HIGH='highp'
*/
export enum PRECISION {
LOW = 'lowp',
MEDIUM = 'mediump',
HIGH = 'highp',
};
/**
* 文本渐变方式,以后用
* @static
* @constant
* @name TEXT_GRADIENT
* @type {object}
* @property {number} LINEAR_VERTICAL 水平渐变
* @property {number} LINEAR_HORIZONTAL 垂直渐变
*/
export enum TEXT_GRADIENT {
LINEAR_VERTICAL = 0,
LINEAR_HORIZONTAL,
};
/**
* 文本对齐方式 水平
*/
export enum TEXT_ALIGN {
CENTER = "center",
LEFT = "left",
RIGHT = "right",
}
export enum VERTICAL_ALIGN {
MIDDLE = "middle",
UP = "up",
DOWN = "down",
TOP = "top",
BOTTOM = "bottom",
}
/**
* 文本类型,单行或多行
*/
export enum TEXT_lINETYPE {
SINGLE = "single",
MULTI = "multi"
}
/**
* 画线时的对齐方式
*/
export enum LINE_ALIGNMENT {
middle = 0.5,
outter = 1,
inner = 0
}
/**
* canvas线头
*/
export enum LINE_CAP {
BUTT = "butt",
ROUND = "round",
SQUARE = "square",
}
/**
* canvas线连接处
*/
export enum LINE_JOIN {
MITER = "miter",
ROUND = "round",
BEVEL = "bevel"
}
/**
* 混色模式,暂不支持使用
* @static
* @constant
* @name BLEND_MODES
* @type {object}
* @property {number} NORMAL
* @property {number} ADD
* @property {number} MULTIPLY
* @property {number} SCREEN
* @property {number} OVERLAY
* @property {number} DARKEN
* @property {number} LIGHTEN
* @property {number} COLOR_DODGE
* @property {number} COLOR_BURN
* @property {number} HARD_LIGHT
* @property {number} SOFT_LIGHT
* @property {number} DIFFERENCE
* @property {number} EXCLUSION
* @property {number} HUE
* @property {number} SATURATION
* @property {number} COLOR
* @property {number} LUMINOSITY
*/
export enum BLEND_MODES {
NORMAL = 0,
ADD,
MULTIPLY,
SCREEN,
OVERLAY,
DARKEN,
LIGHTEN,
COLOR_DODGE,
COLOR_BURN,
HARD_LIGHT,
SOFT_LIGHT,
DIFFERENCE,
EXCLUSION,
HUE,
SATURATION,
COLOR,
LUMINOSITY,
NORMAL_NPM,
ADD_NPM,
SCREEN_NPM,
NONE,
SRC_OVER,
SRC_IN,
SRC_OUT,
SRC_ATOP,
DST_OVER,
DST_IN,
DST_OUT,
DST_ATOP,
ERASE,
SUBTRACT,
};
/**
* webgl的绘制模式枚举
* @static
* @constant
* @name DRAW_MODES
* @type {object}
* @property {number} POINTS
* @property {number} LINES
* @property {number} LINE_LOOP
* @property {number} LINE_STRIP
* @property {number} TRIANGLES
* @property {number} TRIANGLE_STRIP
* @property {number} TRIANGLE_FAN
*/
export enum DRAW_MODES {
POINTS = 0,
LINES,
LINE_LOOP,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
TRIANGLE_FAN,
};
/**
* 纹理的缩放模式
* @static
* @constant
* @name SCALE_MODES
* @type {object}
* @property {number} LINEAR 线性插值光滑缩放
* @property {number} NEAREST 使用临近像素缩放
*/
export enum SCALE_MODES {
LINEAR = 0,
NEAREST,
};
/**
* 纹理的wrap mode
* 非2的幂次纹理只能用CLAMP
* 仅用于webgl模式
* @static
* @constant
* @name WRAP_MODES
* @type {object}
* @property {number} CLAMP - The textures uvs are clamped
* @property {number} REPEAT - The texture uvs tile and repeat
* @property {number} MIRRORED_REPEAT - The texture uvs tile and repeat with mirroring
*/
export enum WRAP_MODES {
CLAMP = 33071,
REPEAT = 10497,
MIRRORED_REPEAT = 33648,
};
/**
* 查看类TextureGarbageCollector
* @static
* @constant
* @name GC_MODES
* @type {object}
* @property {number} AUTO - 周期性自动回收
* @property {number} MANUAL - 手动调用
*/
export enum GC_MODES {
AUTO = 0,
MANUAL,
};
/**
* 其实具体常量在gl上是固定的,但是还是做映射吧,BUFFER_TYPE也是
*/
export enum BUFFER_USAGE {
/**
* 缓冲区的内容可能经常使用,而不会经常更改。内容被写入缓冲区,但不被读取。
*/
STATIC_DRAW = "STATIC_DRAW",
/**
* 缓冲区的内容可能经常被使用,并且经常更改。内容被写入缓冲区,但不被读取。
*/
DYNAMIC_DRAW = "DYNAMIC_DRAW",
/**
* 缓冲区的内容可能不会经常使用。内容被写入缓冲区,但不被读取。
*/
STREAM_DRAW = "STREAM_DRAW"
}
export enum BUFFER_TYPE {
ELEMENT_ARRAY_BUFFER = "ELEMENT_ARRAY_BUFFER",
ARRAY_BUFFER = "ARRAY_BUFFER",
}
//还是存映射,不用gl固定的数值
export enum FORMAT_TYPE {
RGBA = "RGBA",
RGB = "RGB",
}
export enum DATA_TYPE {
UNSIGNED_BYTE = "UNSIGNED_BYTE",
FLOAT = "FLOAT",
UNSIGNED_SHORT = "UNSIGNED_SHORT",
}
export enum TEXTURE_TARGET_TYPE {
TEXTURE_2D = "TEXTURE_2D",
TEXTURE_CUBE_MAP = "TEXTURE_CUBE_MAP",
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
import { Event } from "../events/Event";
import { DisplayObject } from "./DisplayObject";
// import { devicePixelRatio } from "../const";
/**
* canvas上的html标签浮层
* 不能用于容器
* 注意canvas所在标签和自身htmlElement样式position设置为absolute
* @class FloatDisplay
* @extends DisplayObject
* @public
* @since 1.0.0
*/
export class FloatDisplay extends DisplayObject {
/**
* 包装的html元素
* @property htmlElement
* @public
* @since 1.0.0
* @type{HtmlElement}
*/
public htmlElement: any = null;
/**
* 是否已经添加了舞台事件
* @property _isAdded
* @since 1.0.0
* @type {boolean}
* @private
*/
private _isAdded: boolean = false;
/**
* 记录是否需要修改位置矩阵
*/
private _transformID: number = -1;
/**
* 构造函数
* @method FloatDisplay
* @since 1.0.0
* @public
* @example
* //创建悬浮的html元素
* var section = document.createElement('section');
* section.id = "rule";
* section.style.overflowX = "hidden";
* section.style.overflowY = "auto";
* section.style.width = w + "px";
* section.style.height = h + "px";
* section.style.lineHeight = lh + "px";
* section.style.fontFamily = '微软雅黑';
* section.style.fontSize = fs + 'px';
* section.style.color = "#ffffff";
* section.style.position = "absolute";
* //创建Floatview 把我们要悬浮的元素封装进去
* var rule = new FloatDisplay();
* stage.addChild(rule);
* rule.x = ox;
* rule.y = oy;
* rule.init(section);
* section.innerHTML = DataManager.ins.getData("ajaxElement").data.rule;
*
*/
public constructor() {
super();
let s = this;
s._instanceType = "FloatDisplay";
s.addEventListener(Event.REMOVED_FROM_STAGE, function (e: Event) {
if (s.htmlElement) {
s.htmlElement.style.display = "none";
}
//移除updateStyle监听
s.stage.removeEventListener(Event.ENTER_FRAME, s.updateStyle, s)
});
s.addEventListener(Event.ADDED_TO_STAGE, function (e: Event) {
if (s.htmlElement) {
let style = s.htmlElement.style;
if (!s._isAdded) {
s._isAdded = true;
// if (s.stage["rootDiv"]) {
// s.stage["rootDiv"].insertBefore(s.htmlElement, s.stage["rootDiv"].childNodes[0]);
// }
// //没有div直接加在body里吧
// else {
// document.body.appendChild(s.htmlElement)
// }
s.addHtmlElement()
} else {
if (s.htmlElement && s.visible) {
style.display = "block";//"inline-block"
}
}
}
//在stage上监听循环,修改显示
s.stage.addEventListener(Event.ENTER_FRAME, s.updateStyle, s)
});
}
/**
* 初始化方法,htmlElement 一定要设置width和height样式,并且一定要用px单位
* @method init
* @public
* @since 1.0.0
* @param {HtmlElement} htmlElement 需要封装起来的html元素的引用。你可以通过这个引用来调用或设置此元素自身的属性方法和事件,甚至是样式
*/
public init(htmlElement: any): void {
let s = this;
let she: any;
if (typeof (htmlElement) == "string") {
she = document.getElementById(htmlElement);
} else if (htmlElement._instanceType == "Video") {
she = htmlElement.media;
} else {
she = htmlElement;
}
//原先有的先移除
if (s.htmlElement) s.removeHtmlElement();
let style = she.style;
style.position = "absolute";
style.display = "none";
style.transformOrigin = style.WebkitTransformOrigin = "0 0 0";
let ws = s.getStyle(she, "width");
let hs = s.getStyle(she, "height");
let w = 0, h = 0;
if (ws.indexOf("px")) {
w = parseInt(ws);
}
if (hs.indexOf("px")) {
h = parseInt(hs);
}
// s._bounds.width = w;
// s._bounds.height = h;
s._localBoundsSelf.width = w;
s._localBoundsSelf.height = h;
s.htmlElement = she;
//如果原先在舞台上,加入,是否显示下一帧吧
if (s.stage) {
s._isAdded = true;
// if (s.stage["rootDiv"]) {
// s.stage["rootDiv"].insertBefore(s.htmlElement, s.stage["rootDiv"].childNodes[0]);
// }
// //没有div直接加在body里吧
// else {
// document.body.appendChild(s.htmlElement)
// }
s.addHtmlElement()
}
}
/**
* @method getStyle
* @param {HTMLElement} elem
* @param cssName
* @return {any}
*/
private getStyle(elem: HTMLElement, cssName: any): any {
//如果该属性存在于style[]中,则它最近被设置过(且就是当前的)
if (elem.style[cssName]) {
return elem.style[cssName];
}
if (document.defaultView && document.defaultView.getComputedStyle) {
//它使用传统的"text-Align"风格的规则书写方式,而不是"textAlign"
cssName = cssName.replace(/([A-Z])/g, "-$1");
cssName = cssName.toLowerCase();
//获取style对象并取得属性的值(如果存在的话)
let s = document.defaultView.getComputedStyle(elem, "");
return s && s.getPropertyValue(cssName);
}
return null;
}
/**
* 考虑放到stage里,因为父级的visible修改不会调用updateStyle
* @method updateStyle
* @public
* @since 1.1.4
*/
private updateStyle(): void {
let s = this;
let o = s.htmlElement;
if (o) {
let style = o.style;
let visible = s.visible;
//还得考虑是否在stage里
if (!s.stage) {
visible = false
}
if (visible) {
let parent = s.parent;
while (parent) {
if (!parent.visible) {
visible = false;
break;
}
parent = parent.parent;
}
}
let show = visible ? "block" : "none";
if (show != style.display) {
style.display = show;
}
if (visible) {
if (this._transformID != this.transform._worldID) {
this._transformID = this.transform._worldID
let mtx = s.transform.worldMatrix;
let d = s.stage["_dpi"] || s.stage["dpi"]//devicePixelRatio;//不用设备的,改成stage的
style.transform = style.webkitTransform = "matrix(" + (mtx.a / d).toFixed(4) + "," + (mtx.b / d).toFixed(4) + "," + (mtx.c / d).toFixed(4) + "," + (mtx.d / d).toFixed(4) + "," + (mtx.tx / d).toFixed(4) + "," + (mtx.ty / d).toFixed(4) + ")";
}
style.opacity = s._worldAlpha;
}
}
}
/**
* 移除htmlElement
*/
private removeHtmlElement(): void {
let s = this;
let elem = s.htmlElement;
if (elem) {
elem.style.display = "none";
if (elem.parentNode) {
elem.parentNode.removeChild(elem);
}
s._isAdded = false;
s.htmlElement = null;
}
}
//放在stage的帧循环的监听里了
// public renderCanvas() {
// this.updateStyle();
// }
// public renderWebGL() {
// this.updateStyle();
// }
private addHtmlElement() {
const { stage, htmlElement } = this;
if (!stage || !htmlElement) return;
var divParent = this.stage["canvas"].parentNode;
if (divParent) {
divParent.insertBefore(htmlElement, divParent.childNodes[0])
} else {
//没有div直接加在body里
document.body.appendChild(htmlElement)
}
}
public destroy(): void {
super.destroy();
this.removeHtmlElement();
}
}
This diff is collapsed.
This diff is collapsed.
export { default as Container } from "./Container"
export * from "./DisplayObject"
export * from "./FloatDisplay"//淘宝上用不了,考虑怎么加
export { default as Sprite } from "./Sprite"
export * from "./Stage"
\ No newline at end of file
import { HashObject } from "../HashObject";
/**
* 事件类,引擎中一切事件的基类
* @class Event
* @extends AObject
* @public
* @since 1.0.0
*/
export class Event extends HashObject {
/**
* 舞台尺寸发生变化时触发
* @Event
* @property RESIZE
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static RESIZE: string = "onResize";
/**
* Scroll组件滑动到开始位置事件
* @property SCROLL_TO_HEAD
* @static
* @since 1.1.0
* @type {string}
*/
public static SCROLL_TO_HEAD: string = "onScrollToHead";
/**
* Scroll组件停止滑动事件
* @property SCROLL_STOP
* @static
* @since 1.1.0
* @type {string}
*/
public static SCROLL_STOP: string = "onScrollStop";
/**
* Scroll组件开始滑动事件
* @property SCROLL_START
* @static
* @since 1.1.0
* @type {string}
*/
public static SCROLL_START: string = "onScrollStart";
/**
* Scroll组件滑动到结束位置事件
* @property SCROLL_TO_END
* @static
* @since 1.1.0
* @type {string}
*/
public static SCROLL_TO_END: string = "onScrollToEnd";
/**
* Scroll组件滚动时触发
*/
public static SCROLLING: string = "onScrolling";
/**
* 舞台初始化完成后会触发的事件
* @property INIT_STAGE
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static INIT_STAGE: string = "onInitStage";
/**
* 显示对象加入到舞台事件
* @Event
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static ADDED_TO_STAGE: string = "onAddedToStage";
/**
* 显示对象从舞台移出事件
* @Event
* @property REMOVED_FROM_STAGE
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static REMOVED_FROM_STAGE: string = "onRemovedFromStage";
/**
* 显示对象 循环帧事件
* @Event
* @property ENTER_FRAME
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static ENTER_FRAME: string = "onEnterFrame";
/**
* AnimationClip 播放完成事件
* @Event
* @property END_FRAME
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static END_FRAME: string = "onEndFrame";
/**
* 完成事件
* @Event
* @property COMPLETE
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static COMPLETE: string = "onComplete";
/**
* 加载过程事件
* @Event
* @property PROGRESS
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static PROGRESS: string = "onProgress";
/**
* 出错事件
* @Event
* @property ERROR
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static ERROR: string = "onError";
/**
* 输入框失去焦点事件
* @Event
* @property BLUR
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static BLUR: string = "onBlur";
/**
* 输入框聚焦事件
* @Event
* @property FOCUS
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static FOCUS: string = "onFocus";
/**
* 输入框输入事件
* @Event
* @property INPUT
* @type {string}
* @static
* @public
* @since 1.0.0
*/
public static INPUT: string = "onInput";
/**
* 事件类型名
* @property type
* @type {string}
* @public
* @since 1.0.0
*/
public type: string = "";
/**
* 触发此事件的对象
* @property target
* @public
* @since 1.0.0
* @type {any}
*/
public target: any = null;
/**
* 随着事件一起附带的信息对象
* 所有需要随事件一起发送的信息都可以放在此对象中
* @property data
* @public
* @since 1.0.0
* @type {any}
* @default null
*/
public data: any = null;
/**
* @method Event
* @param {string} type 事件类型
* @public
* @since 1.0.0
*/
public constructor(type: string) {
super();
this._instanceType = "Event";
this.type = type;
}
/**
* 防止对事件流中当前节点的后续节点中的所有事件侦听器进行处理。
* 冒泡和捕获都有用
* @method stopPropagation
* @public
* @since 2.0.0
* @return {void}
*/
public stopPropagation(): void {
this._pd = true;
}
/**
* 是否阻止事件向下冒泡
* @property _pd
* @type {boolean}
* @private
* @since 1.0.0
*/
private _pd: boolean = false;
public destroy(): void {
let s = this;
s.target = null;
s.data = null;
}
/**
* 重置事件
* @method reset
* @param {string} type
* @param target
* @since 2.0.0
* @return {void}
* @public
*/
public reset(type: string, target: any): void {
let s = this;
s.target = target;
s._pd = false;
s.type = type;
s.data = null;//最好加上,不然可能带上原来的数据
}
}
This diff is collapsed.
import { EventDispatcher } from "./EventDispatcher";
/**
* 全局事件派发器
*/
export const GDispatcher: EventDispatcher = new EventDispatcher()
//下面这个用习惯了,但是为了统一,还是改吧,engine和fyge里都没改,用着呢有地方
// export class GDispatcher {
// /**
// * 事件回调池
// */
// private static callbackPool: any = {};
// /**
// * 事件作用域池
// */
// private static thisObjPool: any = {};
// /**
// *
// * @param name 事件名
// * @param callback 回调
// * @param thisObj 作用域
// */
// public static addEvent(name: string, callback, thisObj?: any): void {
// if (!this.callbackPool[name]) {
// this.callbackPool[name] = [];
// this.thisObjPool[name] = [];
// }
// const index: number = this.callbackPool[name].indexOf(callback);
// if (index != -1) {
// this.callbackPool[name][index] = callback;
// this.thisObjPool[name][index] = thisObj;
// } else {
// this.callbackPool[name].push(callback);
// this.thisObjPool[name].push(thisObj);
// }
// }
// /**
// *
// * @param name 事件名
// * @param callback 回调
// * @param thisObj 作用域
// */
// public static removeEvent(name: string, callback, thisObj?: any): void {
// if (this.callbackPool[name]) {
// var len = this.callbackPool[name].length;
// for (let i = len - 1; i >= 0; i--) {
// if (this.callbackPool[name][i] === callback && this.thisObjPool[name][i] == thisObj) {
// this.callbackPool[name].splice(i, 1);
// this.thisObjPool[name].splice(i, 1);
// }
// }
// // const index: number = this.callbackPool[name].indexOf(callback);
// // if (index != -1) {
// // this.callbackPool[name].splice(index, 1);
// // this.thisObjPool[name].splice(index, 1);
// // }
// }
// }
// /**
// * 派发事件
// * @param name 事件名
// * @param args 任意参数
// */
// public static dispatchEvent(name: string, ...args): void {
// const callbacks: Function[] = this.callbackPool[name];
// const thisObjs: any = this.thisObjPool[name];
// if (callbacks) {
// let i = 0;
// const len: number = callbacks.length;
// for (i; i < len; i++) {
// callbacks[i].apply(thisObjs[i], args);
// }
// }
// }
// }
\ No newline at end of file
import { Event } from "./Event";
import { DisplayObject } from "../display/DisplayObject";
/**
* 鼠标事件类
* @class MouseEvent
* @extends Event
* @public
* @since 1.0.0
*/
export class MouseEvent extends Event {
/**
* 鼠标按下事件
* @property MOUSE_DOWN
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static MOUSE_DOWN: string = "onMouseDown";
/**
* 鼠标抬起事件
* @property MOUSE_UP
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static MOUSE_UP: string = "onMouseUp";
/**
* 鼠标点击事件
* @property CLICK
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static CLICK: string = "onMouseClick";
/**
* 鼠标移动事件
* @property MOUSE_MOVE
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static MOUSE_MOVE: string = "onMouseMove";
/**
* 鼠标移入到显示对象上里触发的事件
* @property MOUSE_OVER
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static MOUSE_OVER: string = "onMouseOver";
/**
* 鼠标移出显示对象边界触发的事件
* @property MOUSE_OUT
* @static
* @public
* @since 1.0.0
* @type {string}
*/
public static MOUSE_OUT: string = "onMouseOut";
/**
* 鼠标事件时canvas实际尺寸坐标x点
* @property clientX
* @public
* @since 1.0.0
* @type {number}
*/
public clientX: number = 0;
/**
* 鼠标事件时canvas实际尺寸坐标y点
* @property clientY
* @public
* @since 1.0.0
* @type {number}
*/
public clientY: number = 0;
/**
* 鼠标事件时stage上坐标x点
* @property stageX
* @public
* @since 1.0.0
* @type {number}
*/
public stageX: number = 0;
/**
* 鼠标事件时stage上坐标y点
* @property stageY
* @public
* @since 1.0.0
* @type {number}
*/
public stageY: number = 0;
/**
* 鼠标事件时显示对象本地坐标x点
* @property localX
* @public
* @since 1.0.0
* @type {number}
*/
public localX: number = 0;
/**
* 鼠标事件时显示对象本地坐标y点
* @property localY
* @public
* @since 1.0.0
* @type {number}
*/
public localY: number = 0;
/**
* 鼠标事件的终点对象,注意区分target
* @property currentTarget
* @public
* @since 1.0.0
* @type{DisplayObject}
* @default null
*/
public currentTarget: DisplayObject = null;
/**
* 鼠标事件的手指唯一标识
* @property identifier
* @type {number}
* @since 1.1.2
* @public
*/
public identifier: any = 0;
/**
* @method MouseEvent
* @public
* @since 1.0.0
* @param {string} type
*/
public constructor(type: string) {
super(type);
this._instanceType = "MouseEvent";
}
/**
* 销毁
*/
public destroy(): void {
//清除相应的数据引用
let s = this;
s.currentTarget = null;
super.destroy();
}
}
\ No newline at end of file
export * from "./Event";
export * from "./EventDispatcher";
// export * from "./GDispatcher";//不往外导了,因为引擎内部也没有用到,外部如果需要,自行new
export * from "./MouseEvent";
\ No newline at end of file
import { extractUniformsFromSrc } from './extractUniformsFromSrc';
import { BLEND_MODES } from '../const';
import { Shader } from '../renderers/webgl/Shader';
import { FilterManager, FilterState } from '../renderers/managers/FilterManager';
import { RenderTexture } from '../texture';
import { Dict } from '../utils';
import { IShaderUniformData } from '../renderers/webgl/shader/UniformType';
import { defaultFilterMatrixVert, defaultFilterSamplerFrag } from './defaultShaderSrc';
////有部分固定的attr和uniform需要确定
/**
* 基本就是一个着色器,暂时不考虑集成GLShader,还不晓得咋搞
*/
export default class Filter extends Shader {
private _blendMode: BLEND_MODES = BLEND_MODES.NORMAL;
/**
* 混色
*/
get blendMode(): BLEND_MODES {
return this._blendMode;
}
set blendMode(value: BLEND_MODES) {
this._blendMode = value;
}
padding: number = 0;
protected _resolution: number = 1
get resolution(): number {
return this._resolution
};
set resolution(v: number) {
this._resolution = v;
}
enabled: boolean = true;
autoFit: boolean = true;
/**
* @param {string} [vertexSrc]
* @param {string} [fragmentSrc]
* @param {object} [uniformsData] 可以自己传,但是主要以字符串解析为主,尽量不传吧
*/
constructor(
vertexSrc: string = defaultFilterMatrixVert,
fragmentSrc: string = defaultFilterSamplerFrag,
uniformsData?: Dict<IShaderUniformData>
) {
super(
vertexSrc,
fragmentSrc,
uniformsData || extractUniformsFromSrc(vertexSrc, fragmentSrc)
)
}
apply(filterManager: FilterManager, input: RenderTexture, output: RenderTexture, clear: boolean, currentState?: FilterState) {
//子类重写
//自己处理自定义的uniform属性等等
filterManager.applyFilter(this, input, output, clear);
}
}
//默认下顶点着色器都用highp float精度,片元着色器用mediump float
/**
* 最简滤镜顶点着色器,很常用,因为多数是处理片元的
*/
export const defaultFilterVert =
"precision highp float;" +
"attribute vec2 aVertexPosition;" +
"attribute vec2 aTextureCoord;" +
"uniform mat3 projectionMatrix;" +
"varying vec2 vTextureCoord;" +
"void main(void){" +
"gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);" +
"vTextureCoord = aTextureCoord;" +
"}"
/**
* 啥也没干的最简单的片元着色器
*/
export const defaultFilterFrag =
`varying vec2 vTextureCoord;
uniform sampler2D uSampler;
void main() {
gl_FragColor = texture2D( uSampler, vTextureCoord );
}`;
/**
* 带滤镜矩阵的的顶点着色器,用于比如displaceFilter
*/
export const defaultFilterMatrixVert =
"precision highp float;" +
"attribute vec2 aVertexPosition;" +
"attribute vec2 aTextureCoord;" +
"uniform mat3 projectionMatrix;" +
"uniform mat3 filterMatrix;" +
"varying vec2 vTextureCoord;" +
"varying vec2 vFilterCoord;" +
"void main(void){" +
"gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);" +
"vFilterCoord = ( filterMatrix * vec3( aTextureCoord, 1.0) ).xy;" +
"vTextureCoord = aTextureCoord;" +
"}"
/**
* 带滤镜矩阵的的片元着色器,很少用到,基本都是自定义的,做个参考吧
*/
export const defaultFilterSamplerFrag = [
'varying vec2 vTextureCoord;',
'varying vec2 vFilterCoord;',
'uniform sampler2D uSampler;',
'uniform sampler2D filterSampler;',
'void main(void){',
' vec4 masky = texture2D(filterSampler, vFilterCoord);',
' vec4 sample = texture2D(uSampler, vTextureCoord);',
' vec4 color;',
' if(mod(vFilterCoord.x, 1.0) > 0.5)',
' {',
' color = vec4(1.0, 0.0, 0.0, 1.0);',
' }',
' else',
' {',
' color = vec4(0.0, 1.0, 0.0, 1.0);',
' }',
// ' gl_FragColor = vec4(mod(vFilterCoord.x, 1.5), vFilterCoord.y,0.0,1.0);',
' gl_FragColor = mix(sample, masky, 0.5);',
' gl_FragColor *= sample.a;',
'}',
].join('\n');
\ No newline at end of file
import { defaultValue } from "../renderers/webgl/shader/defaultValue";
import { IShaderUniformData } from "../renderers/webgl/shader/UniformType";
import { Dict } from "../utils";
export function extractUniformsFromSrc(vertexSrc, fragmentSrc): Dict<IShaderUniformData> {
const vertUniforms = extractUniformsFromString(vertexSrc);
const fragUniforms = extractUniformsFromString(fragmentSrc);
// return Object["assign"](vertUniforms, fragUniforms);
return { ...vertUniforms, ...fragUniforms };
}
function extractUniformsFromString(string) {
const maskRegex = new RegExp('^(projectionMatrix|uSampler|filterArea|filterClamp)$');
const uniforms = {};
let nameSplit;
// clean the lines a little - remove extra spaces / tabs etc
// then split along ';'
const lines = string.replace(/\s+/g, ' ')
.split(/\s*;\s*/);
// loop through..
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.indexOf('uniform') > -1) {
const splitLine = line.split(' ');
const type = splitLine[1];
let name = splitLine[2];
let size = 1;
if (name.indexOf('[') > -1) {
// array!
nameSplit = name.split(/\[|]/);
name = nameSplit[0];
size *= Number(nameSplit[1]);
}
// if (!name.match(maskRegex)) {//先所有的都解出来
uniforms[name] = {
value: defaultValue(type, size),
type,
name,//name和size其实用不到
size,
};
// }
}
}
return uniforms;
}
/**
* filter模块基本延用pixi模块
* {@link http://pixijs.io/pixi-filters/tools/demo/}
* This namespace contains WebGL-only display filters that can be applied
* to DisplayObjects using the {@link DisplayObject#filters filters} property.
* @example
* // Draw a green rectangle
* const rect = new Graphics()
* .beginFill(0x00ff00)
* .drawRect(40, 40, 200, 200);
*
* // Add a blur filter
* rect.filters = [new filters.BlurFilter()];
*
* 耗性能得很,毕竟要建帧缓存和增加着色器计算负担
* 1、避免滤镜嵌套
* 2、修改屏幕分辨率,为了减小画布
* 3、滤镜范围越大越卡(滤镜宿主需渲染的大小,像素透明度不为0的范围大小)
* 4、sprite遮罩性能比Graphics差(能用Graphics就用Graphics),暂时只有刮刮卡需要
* 5、暂时发现BlurFilter性能很差,着色器多次运行处理帧缓存,建议使用新的BlurFilterNew
* 6、noise在移动端还有点问题,无效果,且变暗
* 7
*/
export { default as Filter } from './Filter';
export { default as SpriteMaskFilter } from './spriteMask/SpriteMaskFilter';
export * from "./defaultShaderSrc";
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
import Circle from "./shapes/Circle";
import { Rectangle, Matrix } from "../math";
import Ellipse from "./shapes/Ellipse";
import Polygon from "./shapes/Polygon";
import RoundedRectangle from "./shapes/RoundedRectangle";
import { HashObject } from "../HashObject";
import LineStyle from "./styles/LineStyle";
import FillStyle from "./styles/FillStyle";
/**
* A GraphicsData object.
* 记录图形数据
*/
export default class GraphicsData extends HashObject {
fillStyle: FillStyle;
lineStyle: LineStyle;
matrix: Matrix;
holes: any[];
/**
* The shape object to draw.
* @member {Circle|Ellipse|Polygon|Rectangle|RoundedRectangle}
*/
shape: any;
/**
* The type of the shape, see the Const.Shapes file for all the existing types,
* @member {number}
*/
type: number;
/**
* 点的一维数组[x,y,x1,y1,x2,y2]
* 存下shape的点数据,因为如果shape不是poly不会特意存下points
*/
points: number[]
/**
*
*/
constructor(
shape: Circle | Rectangle | Ellipse | Polygon | RoundedRectangle,
fillStyle: FillStyle = null,
lineStyle: LineStyle = null,
matrix: Matrix = null
) {
super();
this._instanceType = "GraphicsData"
this.shape = shape;
this.lineStyle = lineStyle;
this.fillStyle = fillStyle;
this.matrix = matrix;
this.holes = [];
this.type = shape.type;
this.points = [];
}
/**
* Creates a new GraphicsData object with the same values as this one.
*
* @return {GraphicsData} Cloned GraphicsData object
*/
clone(): GraphicsData {
return new GraphicsData(
this.shape,
this.fillStyle,
this.lineStyle,
this.matrix
);
}
/**
* Destroys the Graphics data.
*/
destroy() {
this.shape = null;
this.holes.length = 0;
this.holes = null;
this.points.length = 0;
this.points = null;
this.lineStyle = null;
this.fillStyle = null;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
export { default as Graphics } from "./Graphics";
// export * from "./GraphicsData";//暂时不导。外部用不到暂时
export * from "./Shape";
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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