|
After Width: | Height: | Size: 354 KiB |
@ -0,0 +1,229 @@ |
|||
/* |
|||
author: leeenx |
|||
@ 贪吃蛇的 control 类 |
|||
*/ |
|||
|
|||
// 游戏通用ticker
|
|||
import ticker from '../lib/utils/ticker'; |
|||
|
|||
// timer
|
|||
import timer from '../lib/utils/timer'; |
|||
|
|||
// 随机打散数组
|
|||
import randomList from '../lib/utils/randomList'; |
|||
|
|||
// 事件
|
|||
import Events from '../lib/utils/events'; |
|||
|
|||
// control 类
|
|||
export default class control { |
|||
// 构建函数
|
|||
constructor(model, view) { |
|||
this.model = model; |
|||
this.view = view; |
|||
|
|||
// 挂载一个 speed 属性
|
|||
Reflect.defineProperty(this, "speed", { |
|||
get: function() { |
|||
return this.speedScalar || 1; |
|||
}, |
|||
set: function(value) { |
|||
if(this.speedScalar !== value) { |
|||
this.speedScalar = value; |
|||
this.interval = 300 / this.speedScalar; |
|||
// 更新 timer
|
|||
timer.set(this.intervalID, {delay: this.interval}); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// tickHandle 绑定当前 this
|
|||
this.tickHandle = this.tickHandle.bind(this); |
|||
|
|||
// this.update 绑定 this
|
|||
this.update = this.update.bind(this); |
|||
|
|||
// 挂载事件对象
|
|||
this.event = new Events(); |
|||
|
|||
// 四个方向
|
|||
this.fourDirections = ["left", "up", "right", "down"]; |
|||
} |
|||
|
|||
// 初始化
|
|||
init(config = {}) { |
|||
// 添加 ticker
|
|||
ticker.addEventListener("tick", this.tickHandle); |
|||
// 默认暂停 ticker
|
|||
this.pause(); |
|||
|
|||
let { |
|||
width = 640, |
|||
height = 640, |
|||
row = 50, |
|||
column = 50, |
|||
border = 0x999999, |
|||
color = 0x000000, // 蛇的节点颜色
|
|||
food = color, // 食物颜色
|
|||
min = 3, // 初始长度
|
|||
speed = 1 // 速度标量
|
|||
} = config; |
|||
|
|||
// 存一份 config 到 this
|
|||
this.config = config; |
|||
|
|||
// 初始化 model
|
|||
this.model.init({row, column, min}); |
|||
|
|||
// view.data
|
|||
let data = { |
|||
zone: this.model.zone, |
|||
snake: this.model.snake, |
|||
food: this.model.food |
|||
}; |
|||
|
|||
// 初始化 view
|
|||
this.view.init({width, height, row, column, border, color, food, data}); |
|||
|
|||
// interval 的间隔
|
|||
this.interval = 300 / this.speedScalar ; |
|||
|
|||
// 定时更新view
|
|||
this.intervalID = timer.setInterval(this.update, this.interval); |
|||
|
|||
// 速度标量
|
|||
this.speed = speed; |
|||
|
|||
// 蛇长度
|
|||
this.length = this.model.snake.length; |
|||
|
|||
// 初始化食物
|
|||
this.food = this.model.food; |
|||
|
|||
// 用户操作的方向列表
|
|||
this.directions = []; |
|||
|
|||
// 总计时
|
|||
if(config.time > 0) { |
|||
let time = config.time / 1000; |
|||
timer.setTimeout(() => this.gameover("timeout"), config.time); |
|||
// 倒数
|
|||
timer.setInterval(() => this.event.dispatch("countdown", --time), 1000); |
|||
} |
|||
|
|||
} |
|||
// 销毁
|
|||
destroy() { |
|||
// 移除 ticker
|
|||
ticker.removeEventListener("tick", this.tickHandle); |
|||
// 清空 timer
|
|||
timer.clean(); |
|||
// 销毁 model
|
|||
this.model.destroy(); |
|||
// 销毁 view
|
|||
this.view.destroy(); |
|||
// GAMEOVER
|
|||
this.GAMEOVER = false |
|||
} |
|||
|
|||
// 转向
|
|||
turn(direction) { |
|||
// 只保存第一次方向操作
|
|||
if(this.fourDirections.indexOf[direction] === -1) return; |
|||
let directionA = direction, directionB = this.directions[0] || this.direction; |
|||
// 给操作列表加个容积 5
|
|||
if(this.directions.length < 5 && directionA !== directionB && !this.isAdverse(directionA, directionB)) { |
|||
this.directions.unshift(directionA); |
|||
} |
|||
} |
|||
|
|||
// 判断两个方向是否相反
|
|||
isAdverse(directionA, directionB) { |
|||
let indexA = this.fourDirections.indexOf(directionA), |
|||
indexB = this.fourDirections.indexOf(directionB); |
|||
if(Math.abs(indexA - indexB) === 2) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// 暂停
|
|||
pause() { |
|||
if(this.GAMEOVER) return ; |
|||
ticker.pause(); |
|||
} |
|||
|
|||
// 恢复
|
|||
resume() { |
|||
if(this.GAMEOVER) return ; |
|||
ticker.resume(); |
|||
} |
|||
|
|||
// start
|
|||
start() { |
|||
if(this.GAMEOVER) return ; |
|||
// this.resume();
|
|||
// 蛇的随机运动方向
|
|||
let {leader, zone} = this.model; |
|||
// 控制方向的变量是 this.direction。this.nextDirection 表示下一个方向
|
|||
this.directions.push( |
|||
randomList( |
|||
this.fourDirections |
|||
.filter( |
|||
(item) => leader[item] !== -1 && zone[leader[item]].fill === undefined |
|||
), |
|||
1 |
|||
) |
|||
); |
|||
|
|||
this.update(); |
|||
} |
|||
|
|||
// 重新开始
|
|||
restart() { |
|||
this.destroy(); |
|||
this.init(this.config); |
|||
this.start(); |
|||
} |
|||
|
|||
// gameover
|
|||
gameover(type) { |
|||
if(this.GAMEOVER) return ; |
|||
this.event.dispatch("gameover", type); |
|||
this.pause(); |
|||
this.GAMEOVER = true; |
|||
} |
|||
|
|||
// update
|
|||
update() { |
|||
// this.direction 表示蛇头节点的运动方向
|
|||
this.direction = this.directions.pop() || this.direction; |
|||
this.model.move(this.direction); |
|||
if(this.model.bar !== undefined) { |
|||
// gameover
|
|||
this.gameover(this.model.bar); |
|||
} |
|||
let data = {snake: this.model.snake, food: this.model.food}; |
|||
if(this.model.dirty) { |
|||
// model 有变化
|
|||
let hasEatEvent = false; |
|||
if(this.food !== this.model.food) { |
|||
this.food = this.model.food; |
|||
hasEatEvent = true; |
|||
this.event.dispatch("before-eat"); |
|||
this.length = this.model.snake.length; |
|||
} |
|||
this.view.update(data); |
|||
hasEatEvent && this.event.dispatch("eat"); |
|||
this.model.cleanDirty(); |
|||
} |
|||
} |
|||
|
|||
// tickHandle
|
|||
tickHandle() { |
|||
timer.update(ticker.paused, ticker.elapsedMS * 1000); |
|||
if(!ticker.paused) { |
|||
this.view.updateTicker(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,208 @@ |
|||
/* |
|||
author: leeenx |
|||
@ 贪吃蛇的 model 类 |
|||
*/ |
|||
|
|||
// 随机打散数组
|
|||
import randomList from '../lib/utils/randomList'; |
|||
|
|||
// 链表类
|
|||
import Chain from '../lib/utils/Chain'; |
|||
|
|||
// model 类
|
|||
export default class model { |
|||
// 构造函数
|
|||
constructor() { |
|||
// 活动空间
|
|||
this.zone = []; |
|||
|
|||
// 蛇链表
|
|||
this.snake = new Chain(); |
|||
|
|||
// 封装 snake 的 unshift, push, shift, pop 方法
|
|||
let {unshift, push, shift, pop} = this.snake; |
|||
|
|||
this.snake.unshift = (index) => { |
|||
unshift.call(this.snake, index); |
|||
// 更新 zone
|
|||
this.updateZone(index, "snake", "unshift"); |
|||
} |
|||
|
|||
this.snake.shift = () => { |
|||
let index = shift.call(this.snake).data; |
|||
// 更新 zone
|
|||
this.updateZone(index, undefined, "shift"); |
|||
} |
|||
|
|||
this.snake.push = (index) => { |
|||
push.call(this.snake, index); |
|||
// 更新 zone
|
|||
this.updateZone(index, "snake", "push"); |
|||
} |
|||
|
|||
this.snake.pop = () => { |
|||
let index = pop.call(this.snake).data; |
|||
// 更新 zone
|
|||
this.updateZone(index, undefined, "pop"); |
|||
} |
|||
|
|||
// 投食后自动更新 zone
|
|||
Reflect.defineProperty(this, "food", { |
|||
get: () => { |
|||
return Reflect.get(this, "_food") |
|||
}, |
|||
set: (value) => { |
|||
// 将值记录到 _food
|
|||
Reflect.set(this, "_food", value); |
|||
// 更新 zone
|
|||
value !== undefined && this.updateZone(value, "food"); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// 初始化
|
|||
init(config) { |
|||
// 指定 zone 长度
|
|||
this.zone.length = config.row * config.column; |
|||
|
|||
// 填充 zone 的初始信息
|
|||
for(let i = 0, len = this.zone.length; i < len; ++i) { |
|||
let [col, row] = [i % config.column, (i / config.row) >> 0] |
|||
this.zone[i] = { |
|||
col: col, |
|||
row: row, |
|||
left: col > 0 ? i - 1 : -1, |
|||
right: col < config.column - 1 ? i + 1 : -1, |
|||
up: row > 0 ? i - config.column : -1, |
|||
down: row < config.row - 1 ? i + config.column : -1 |
|||
} |
|||
} |
|||
|
|||
// 初始蛇的长度
|
|||
while(this.snake.length < config.min) { |
|||
let index = this.snake.length ? this.neighbour() : (Math.random() * this.zone.length)>>0; |
|||
this.snake.unshift(index); |
|||
} |
|||
// 投食
|
|||
this.feed(); |
|||
} |
|||
|
|||
// 销毁
|
|||
destroy() { |
|||
// 清空 zone 内容
|
|||
this.zone = []; |
|||
// 清空链表数组
|
|||
this.snake.clean(); |
|||
// 清除食物
|
|||
this.food = undefined; |
|||
// 清空 bar
|
|||
delete this.bar; |
|||
} |
|||
|
|||
// 邻居元素
|
|||
neighbour() { |
|||
return randomList( |
|||
[ |
|||
this.leader.left, |
|||
this.leader.right, |
|||
this.leader.up, |
|||
this.leader.down |
|||
], |
|||
1, |
|||
(index) => index !== -1 && this.zone[index].fill === undefined |
|||
); |
|||
} |
|||
|
|||
// zone 区域更新状态
|
|||
updateZone(index, fill, type) { |
|||
// console.log(index, fill, type);
|
|||
// fill == undefine 表示 free
|
|||
this.zone[index].fill = fill; |
|||
// leader 更新
|
|||
this.updateLeader(); |
|||
} |
|||
|
|||
// 更新蛇头在 zone 的坐标
|
|||
updateLeader() { |
|||
if(this.snake.length !== 0) { |
|||
this.leader = this.zone[this.snake.first().data]; |
|||
} |
|||
this.dirty = true; |
|||
} |
|||
|
|||
// 清理dirty
|
|||
cleanDirty() { |
|||
this.dirty = false; |
|||
} |
|||
|
|||
// 蛇运动
|
|||
move(direction) { |
|||
let index = this.leader[direction], skipTail = false; |
|||
if(-1 === index) { |
|||
// 撞墙
|
|||
this.collision("bounds"); |
|||
return ; |
|||
} |
|||
if(this.snake.last().data === index) { |
|||
// 即将撞上的尾巴
|
|||
skipTail = true; |
|||
} |
|||
let next = this.zone[index]; |
|||
switch(next.fill) { |
|||
// 吃食
|
|||
case "food": this.eat(); break; |
|||
// 撞到自己
|
|||
case "snake": { |
|||
// 判断是否咬尾
|
|||
if(!skipTail) { |
|||
this.collision("self"); break; |
|||
} |
|||
} |
|||
// 默认前进
|
|||
default: this.snake.pop() & this.snake.unshift(index); |
|||
} |
|||
} |
|||
|
|||
// 吃食
|
|||
eat() { |
|||
// 食物变成了头
|
|||
this.snake.unshift(this.food); |
|||
// 重新投食
|
|||
this.feed(); |
|||
} |
|||
|
|||
// 撞到东西
|
|||
collision(bar) { |
|||
// bar 不为空就认为游戏结束
|
|||
this.bar = bar; |
|||
} |
|||
|
|||
// 赌博
|
|||
bet() { |
|||
let rnd = Math.random() * this.zone.length >> 0; |
|||
return this.zone[rnd].fill === undefined ? rnd : -1; |
|||
} |
|||
|
|||
// 随机喂食
|
|||
feed() { |
|||
// 赌一次
|
|||
let rnd = this.bet(); |
|||
if(rnd !== -1) { |
|||
this.food = rnd; |
|||
return; |
|||
} |
|||
let index = 0, |
|||
count = 0, |
|||
len = this.zone.length - this.snake.length; |
|||
rnd = (Math.random() * count>>0) + 1; |
|||
// 无法投食
|
|||
if(0 === len) { |
|||
this.food = undefined; |
|||
return ; |
|||
} |
|||
while(rnd !== count) { |
|||
this.zone[index++].fill === undefined && ++count; |
|||
} |
|||
this.food = index - 1; |
|||
} |
|||
} |
|||
@ -0,0 +1,315 @@ |
|||
/* |
|||
author: leeenx |
|||
@ 贪吃蛇的 view 类 |
|||
*/ |
|||
|
|||
// 获取 Graphics 的 content-box
|
|||
import "../lib/utils/getContentBoxSize"; |
|||
|
|||
// 不显示 PIXI 信息
|
|||
import "../lib/utils/noHello"; |
|||
|
|||
// 链表类
|
|||
import Chain from "../lib/utils/Chain"; |
|||
|
|||
// view 类
|
|||
export default class view { |
|||
// 构造函数
|
|||
constructor(dom, width, height) { |
|||
// 创建一个 app
|
|||
let app = new PIXI.Application(width, height, { |
|||
transparent: true, |
|||
}); |
|||
|
|||
// canvas 添加到 page
|
|||
dom.appendChild(app.view); |
|||
|
|||
// 销毁pixijs的ticker
|
|||
app.ticker.destroy(); |
|||
|
|||
// 创建 view 的蛇
|
|||
this.snake = new Chain(); |
|||
|
|||
// shift & unshift & pop & push & insertAfter 自动关联 addNode & removeNode
|
|||
let { shift, unshift, pop, push, insertAfter } = this.snake; |
|||
|
|||
// 封闭 shift
|
|||
this.snake.shift = () => { |
|||
// 回收尾节点
|
|||
this.collect(shift.call(this.snake).node); |
|||
}; |
|||
|
|||
// 封装 unshift
|
|||
this.snake.unshift = (data) => { |
|||
unshift.call(this.snake, data); |
|||
let node = (this.snake.first().node = this.calloc()); |
|||
node.setPostion(...this.getPosition(data)); |
|||
}; |
|||
|
|||
// 封闭 pop
|
|||
this.snake.pop = () => { |
|||
// 回收尾节点
|
|||
this.collect(pop.call(this.snake).node); |
|||
}; |
|||
|
|||
// 封装 push
|
|||
this.snake.push = (data) => { |
|||
push.call(this.snake, data); |
|||
let node = (this.snake.last().node = this.calloc()); |
|||
node.setPostion(...this.getPosition(data)); |
|||
}; |
|||
|
|||
// 封装 insertAfter
|
|||
this.snake.insertAfter = (index, data) => { |
|||
insertAfter.call(this.snake, index, data); |
|||
let node = (this.snake.at(index + 1).node = this.calloc()); |
|||
node.setPostion(...this.getPosition(data)); |
|||
}; |
|||
|
|||
// 回收节点
|
|||
this.collection = []; |
|||
|
|||
// 保证 updateTicker 指针永远指向 view
|
|||
this.updateTicker = this.updateTicker.bind(this); |
|||
|
|||
// 挂载到this
|
|||
this.app = app; |
|||
this.stage = app.stage; |
|||
|
|||
// 扩展 PIXI.Graphics
|
|||
PIXI.Graphics.prototype.setPostion = function (x, y = x) { |
|||
this.x = x + this.pivot.x; |
|||
this.y = y + this.pivot.y; |
|||
}; |
|||
} |
|||
|
|||
// 初始化
|
|||
init(config = {}) { |
|||
// pixijs
|
|||
let { |
|||
app, |
|||
app: { stage, renderer }, |
|||
} = this; |
|||
|
|||
// 蛇的尺寸挂载到 config
|
|||
config.size = { |
|||
width: config.width / config.column, |
|||
height: config.height / config.row, |
|||
}; |
|||
console.log(config.size); |
|||
// 初化data
|
|||
this.data = config.data; |
|||
|
|||
// 全局 config 挂载
|
|||
this.config = config; |
|||
|
|||
// 游戏活动区
|
|||
stage.addChild((this.zone = new PIXI.Container())); |
|||
|
|||
// 绘制边界
|
|||
// this.drawBounds();
|
|||
|
|||
// 食物
|
|||
this.food = this.calloc(); |
|||
this.food.visible = false; |
|||
|
|||
// 食物动画 - blink
|
|||
let [from, to] = [ |
|||
{ |
|||
alpha: 1, |
|||
}, |
|||
{ |
|||
alpha: 0, |
|||
}, |
|||
]; |
|||
TweenMax.fromTo(this.food, 0.2, from, to).repeat(-1).yoyo(true); |
|||
|
|||
// 通过 model.zone 创建一张快速定位表
|
|||
this.createQuickMap(this.data.zone); |
|||
|
|||
// 同步 model 的初始数据
|
|||
for (let { data } of this.data.snake) { |
|||
this.snake.push(data); |
|||
} |
|||
} |
|||
|
|||
destroy() { |
|||
let { |
|||
app, |
|||
app: { stage }, |
|||
} = this; |
|||
// 销毁所有子节点
|
|||
for (let child of stage.children) { |
|||
child.destroy(); |
|||
} |
|||
stage.removeChildren(); |
|||
this.collection = []; |
|||
this.snake.clean(); |
|||
} |
|||
// 绘制四条边界
|
|||
drawBounds() { |
|||
let { |
|||
app, |
|||
app: { stage }, |
|||
} = this, |
|||
{ border, width, height } = this.config, |
|||
thickness = 8; |
|||
let bounds = new PIXI.Graphics() |
|||
.beginFill(0xffffff, 1) |
|||
.lineStyle(thickness, border, 1) |
|||
.drawRect(0, 0, width + thickness, height + thickness); |
|||
|
|||
bounds.x = bounds.y = (app.view.width - bounds.cwidth) / 2; |
|||
|
|||
stage.addChild(bounds); |
|||
// bounds 的 index
|
|||
stage.setChildIndex(bounds, 0); |
|||
|
|||
// 活动空间定位
|
|||
[this.zone.x, this.zone.y] = [ |
|||
bounds.x + thickness / 2, |
|||
bounds.y + thickness / 2, |
|||
]; |
|||
} |
|||
|
|||
// 快速寻位表
|
|||
createQuickMap(map) { |
|||
let { width, height } = this.config.size; |
|||
// 快速表
|
|||
this.quickMap = []; |
|||
for (let { col, row } of map) { |
|||
this.quickMap.push([col * width, row * height]); |
|||
} |
|||
} |
|||
|
|||
// 快速计算position
|
|||
getPosition(index) { |
|||
return this.quickMap[index]; |
|||
} |
|||
|
|||
// 创建节点
|
|||
calloc() { |
|||
let node; |
|||
if (this.collection.length === 0) { |
|||
node = new PIXI.Graphics(); |
|||
let { width, height } = this.config.size; |
|||
node.beginFill(this.config.color, 1).drawRect(0, 0, width, height); |
|||
node.pivot.set(width / 2, height / 2); |
|||
node.setPostion(0); |
|||
} else { |
|||
node = this.collection.pop(); |
|||
} |
|||
|
|||
// 默认显示在容器里
|
|||
this.zone.addChild(node); |
|||
return node; |
|||
} |
|||
|
|||
// 回收节点
|
|||
collect(node) { |
|||
node && this.collection.push(node) & this.zone.removeChild(node); |
|||
} |
|||
|
|||
// 随机生成食物
|
|||
feed(index) { |
|||
this.food.visible = 1; |
|||
this.food.graphicsData[0].fillColor = this.config.food; |
|||
this.food.dirty++; |
|||
this.food.clearDirty++; |
|||
this.food.setPostion(...this.getPosition(index)); |
|||
} |
|||
|
|||
// ticker update
|
|||
updateTicker() { |
|||
// this.render();
|
|||
} |
|||
|
|||
// 状态更新
|
|||
update(data) { |
|||
// 食物更新
|
|||
this.food !== data.food && this.feed(data.food); |
|||
this.updateDelta(data.snake); |
|||
} |
|||
|
|||
// 增量更新
|
|||
updateDelta(snakeA, snakeB = this.snake) { |
|||
// snakeA === model.snake, snakeB === view.snake
|
|||
this.updateTail(snakeA, snakeB) |
|||
.then(() => this.updateHead(snakeA, snakeB)) |
|||
.catch(() => this.wholeUpdate(snakeA, snakeB)) |
|||
.then(() => this.render()); |
|||
} |
|||
|
|||
// 检查蛇头
|
|||
updateHead(snakeA, snakeB) { |
|||
return new Promise((resolve, reject) => { |
|||
// snakeA 与 snakeB 做头指针比较
|
|||
let headA, |
|||
headB = snakeB.first(); |
|||
// 指针指向头部
|
|||
snakeA.setPointer(snakeA.HEAD); |
|||
while (snakeB.length <= snakeA.length) { |
|||
headA = snakeA.next(); |
|||
// 头节点匹配
|
|||
if (headA.data === headB.data) { |
|||
// 执行 then 通道
|
|||
return resolve(); |
|||
} |
|||
// 不匹配
|
|||
else { |
|||
// 向snakeB插入头节点
|
|||
if (snakeA.HEAD === headA.index) { |
|||
snakeB.unshift(headA.data); |
|||
} |
|||
// 向snakeB插入第二个节点
|
|||
else { |
|||
snakeB.insertAfter(0, headA.data); |
|||
} |
|||
} |
|||
} |
|||
// 头指针未匹配上,走 catch 通道
|
|||
reject(); |
|||
}); |
|||
} |
|||
|
|||
// 检查蛇尾
|
|||
updateTail(snakeA, snakeB) { |
|||
return new Promise((resolve, reject) => { |
|||
// snakeA 与 snakeB 做尾指针比较
|
|||
let tailA = snakeA.last(), |
|||
tailB; |
|||
while (snakeB.length !== 0) { |
|||
tailB = snakeB.last(); |
|||
// 尾节点匹配
|
|||
if (tailA.data === tailB.data) { |
|||
// 执行 then 通道
|
|||
return resolve(); |
|||
} |
|||
// 不匹配
|
|||
else { |
|||
snakeB.pop(); |
|||
} |
|||
} |
|||
// 尾指针未匹配上,走 catch 通道
|
|||
reject(); |
|||
}); |
|||
} |
|||
|
|||
// 全量更新
|
|||
wholeUpdate(snakeA, snakeB) { |
|||
console.log(">>>>>>>>>>>>>>>>>", "low performance"); |
|||
// 把视图上的蛇回收
|
|||
while (snakeB.length !== 0) { |
|||
snakeB.pop(); |
|||
} |
|||
// 重头开始插入 snake
|
|||
for (let { data } of snakeA) { |
|||
snakeB.unshift(data); |
|||
} |
|||
} |
|||
|
|||
// 渲染
|
|||
render() { |
|||
this.app.renderer.render(this.app.stage); |
|||
} |
|||
} |
|||
@ -0,0 +1,184 @@ |
|||
@charset "UTF-8"; |
|||
|
|||
html,body { |
|||
position: relative; |
|||
width: 100%; |
|||
max-width: 540px; |
|||
height: 100%; |
|||
margin: 0; |
|||
padding: 0; |
|||
background-color: #ece060; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
.snake-game { |
|||
position: relative; |
|||
width: 100%; |
|||
height: auto; |
|||
} |
|||
|
|||
.snake-game canvas { |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: auto; |
|||
} |
|||
|
|||
.snake-controller { |
|||
position: absolute; |
|||
top: 60%; |
|||
left: 0; |
|||
width: 100%; |
|||
} |
|||
|
|||
.snake-switch { |
|||
position: absolute; |
|||
width: 50px; |
|||
height: 53px; |
|||
background: url(../images/switch@2x.png) 0 0 no-repeat; |
|||
background-size: 100% 100%; |
|||
top: 160px; |
|||
right: 30px; |
|||
border: 0 none; |
|||
appearance: none; |
|||
-webkit-appearance: none; |
|||
outline: 0; |
|||
} |
|||
|
|||
.snake-trigger { |
|||
position: absolute; |
|||
width: 50px; |
|||
height: 50px; |
|||
background: url(../images/pause@2x.png) 0 0 no-repeat; |
|||
background-size: 100% 100%; |
|||
top: 160px; |
|||
left: 30px; |
|||
border: 0 none; |
|||
appearance: none; |
|||
-webkit-appearance: none; |
|||
outline: 0; |
|||
} |
|||
.snake-trigger:checked { |
|||
background-image: url(../images/play@2x.png); |
|||
} |
|||
|
|||
.snake-direction { |
|||
position: absolute; |
|||
left: 50%; |
|||
top: 0; |
|||
width: 160px; |
|||
height: 160px; |
|||
margin-left: -80px; |
|||
border: 2px solid #414042; |
|||
background-color: #414042; |
|||
border-radius: 100%; |
|||
overflow: hidden; |
|||
transform: rotateZ(45deg); |
|||
-webkit-transform: rotateZ(45deg); |
|||
} |
|||
|
|||
.snake-up, .snake-right, .snake-down, .snake-left { |
|||
position: absolute; |
|||
width: 80px; |
|||
height: 80px; |
|||
background-color: #ddd; |
|||
} |
|||
|
|||
.snake-up::after, |
|||
.snake-right::after, |
|||
.snake-down::after, |
|||
.snake-left::after { |
|||
content: ''; |
|||
position: absolute; |
|||
left: 40px; |
|||
top: 40px; |
|||
width: 10px; |
|||
height: 10px; |
|||
border-width: 2px 0 0 2px; |
|||
border-color: #414042; |
|||
border-style: solid; |
|||
transform-origin: left top; |
|||
-webkit-transform-origin: left top; |
|||
} |
|||
|
|||
.snake-up { |
|||
top: -1px; |
|||
left: -1px; |
|||
} |
|||
|
|||
.snake-right { |
|||
top: -1px; |
|||
right: -1px; |
|||
} |
|||
.snake-right::after { |
|||
transform: rotate(90deg); |
|||
-webkit-transform: rotate(90deg); |
|||
} |
|||
|
|||
.snake-down { |
|||
bottom: -1px; |
|||
right: -1px; |
|||
} |
|||
|
|||
.snake-down::after { |
|||
transform: rotate(180deg); |
|||
-webkit-transform: rotate(180deg); |
|||
} |
|||
|
|||
.snake-left { |
|||
bottom: -1px; |
|||
left: -1px; |
|||
} |
|||
|
|||
.snake-left::after { |
|||
transform: rotate(270deg); |
|||
-webkit-transform: rotate(270deg); |
|||
} |
|||
|
|||
.snake-direction.up .snake-up, |
|||
.snake-direction.right .snake-right, |
|||
.snake-direction.down .snake-down, |
|||
.snake-direction.left .snake-left |
|||
{ |
|||
background-color: rgba(188, 188, 188, .7); |
|||
} |
|||
|
|||
.snake-timer { |
|||
position: absolute; |
|||
top: 172px; |
|||
right: 0; |
|||
left: 0; |
|||
margin: 0 auto; |
|||
width: 200px; |
|||
text-align: center; |
|||
font-size: 16px; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.snake-speed { |
|||
position: absolute; |
|||
top: 206px; |
|||
right: 0; |
|||
left: 0; |
|||
margin: 0 auto; |
|||
width: 120px; |
|||
height: 4px; |
|||
border-radius: 2px; |
|||
background-color: #ffeeee; |
|||
box-shadow: inset 1px 1px 3px -1px rgba(0, 0, 0, .3); |
|||
} |
|||
|
|||
.snake-speed-thumb { |
|||
position: absolute; |
|||
top: -3px; |
|||
left: 50%; |
|||
width: 10px; |
|||
height: 10px; |
|||
border-radius: 100%; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, .3); |
|||
background-color: #fff; |
|||
} |
|||
|
|||
|
|||
|
|||
@ -0,0 +1,786 @@ |
|||
/*! |
|||
* VERSION: 1.20.2 |
|||
* DATE: 2017-06-30 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) { |
|||
|
|||
var TimelineLite = function(vars) { |
|||
SimpleTimeline.call(this, vars); |
|||
this._labels = {}; |
|||
this.autoRemoveChildren = (this.vars.autoRemoveChildren === true); |
|||
this.smoothChildTiming = (this.vars.smoothChildTiming === true); |
|||
this._sortChildren = true; |
|||
this._onUpdate = this.vars.onUpdate; |
|||
var v = this.vars, |
|||
val, p; |
|||
for (p in v) { |
|||
val = v[p]; |
|||
if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) { |
|||
v[p] = this._swapSelfInParams(val); |
|||
} |
|||
} |
|||
if (_isArray(v.tweens)) { |
|||
this.add(v.tweens, 0, v.align, v.stagger); |
|||
} |
|||
}, |
|||
_tinyNum = 0.0000000001, |
|||
TweenLiteInternals = TweenLite._internals, |
|||
_internals = TimelineLite._internals = {}, |
|||
_isSelector = TweenLiteInternals.isSelector, |
|||
_isArray = TweenLiteInternals.isArray, |
|||
_lazyTweens = TweenLiteInternals.lazyTweens, |
|||
_lazyRender = TweenLiteInternals.lazyRender, |
|||
_globals = _gsScope._gsDefine.globals, |
|||
_copy = function(vars) { |
|||
var copy = {}, p; |
|||
for (p in vars) { |
|||
copy[p] = vars[p]; |
|||
} |
|||
return copy; |
|||
}, |
|||
_applyCycle = function(vars, targets, i) { |
|||
var alt = vars.cycle, |
|||
p, val; |
|||
for (p in alt) { |
|||
val = alt[p]; |
|||
vars[p] = (typeof(val) === "function") ? val(i, targets[i]) : val[i % val.length]; |
|||
} |
|||
delete vars.cycle; |
|||
}, |
|||
_pauseCallback = _internals.pauseCallback = function() {}, |
|||
_slice = function(a) { //don't use [].slice because that doesn't work in IE8 with a NodeList that's returned by querySelectorAll()
|
|||
var b = [], |
|||
l = a.length, |
|||
i; |
|||
for (i = 0; i !== l; b.push(a[i++])); |
|||
return b; |
|||
}, |
|||
p = TimelineLite.prototype = new SimpleTimeline(); |
|||
|
|||
TimelineLite.version = "1.20.2"; |
|||
p.constructor = TimelineLite; |
|||
p.kill()._gc = p._forcingPlayhead = p._hasPause = false; |
|||
|
|||
/* might use later... |
|||
//translates a local time inside an animation to the corresponding time on the root/global timeline, factoring in all nesting and timeScales.
|
|||
function localToGlobal(time, animation) { |
|||
while (animation) { |
|||
time = (time / animation._timeScale) + animation._startTime; |
|||
animation = animation.timeline; |
|||
} |
|||
return time; |
|||
} |
|||
|
|||
//translates the supplied time on the root/global timeline into the corresponding local time inside a particular animation, factoring in all nesting and timeScales
|
|||
function globalToLocal(time, animation) { |
|||
var scale = 1; |
|||
time -= localToGlobal(0, animation); |
|||
while (animation) { |
|||
scale *= animation._timeScale; |
|||
animation = animation.timeline; |
|||
} |
|||
return time * scale; |
|||
} |
|||
*/ |
|||
|
|||
p.to = function(target, duration, vars, position) { |
|||
var Engine = (vars.repeat && _globals.TweenMax) || TweenLite; |
|||
return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position); |
|||
}; |
|||
|
|||
p.from = function(target, duration, vars, position) { |
|||
return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position); |
|||
}; |
|||
|
|||
p.fromTo = function(target, duration, fromVars, toVars, position) { |
|||
var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite; |
|||
return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position); |
|||
}; |
|||
|
|||
p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { |
|||
var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, callbackScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}), |
|||
cycle = vars.cycle, |
|||
copy, i; |
|||
if (typeof(targets) === "string") { |
|||
targets = TweenLite.selector(targets) || targets; |
|||
} |
|||
targets = targets || []; |
|||
if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array.
|
|||
targets = _slice(targets); |
|||
} |
|||
stagger = stagger || 0; |
|||
if (stagger < 0) { |
|||
targets = _slice(targets); |
|||
targets.reverse(); |
|||
stagger *= -1; |
|||
} |
|||
for (i = 0; i < targets.length; i++) { |
|||
copy = _copy(vars); |
|||
if (copy.startAt) { |
|||
copy.startAt = _copy(copy.startAt); |
|||
if (copy.startAt.cycle) { |
|||
_applyCycle(copy.startAt, targets, i); |
|||
} |
|||
} |
|||
if (cycle) { |
|||
_applyCycle(copy, targets, i); |
|||
if (copy.duration != null) { |
|||
duration = copy.duration; |
|||
delete copy.duration; |
|||
} |
|||
} |
|||
tl.to(targets[i], duration, copy, i * stagger); |
|||
} |
|||
return this.add(tl, position); |
|||
}; |
|||
|
|||
p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { |
|||
vars.immediateRender = (vars.immediateRender != false); |
|||
vars.runBackwards = true; |
|||
return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); |
|||
}; |
|||
|
|||
p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) { |
|||
toVars.startAt = fromVars; |
|||
toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false); |
|||
return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope); |
|||
}; |
|||
|
|||
p.call = function(callback, params, scope, position) { |
|||
return this.add( TweenLite.delayedCall(0, callback, params, scope), position); |
|||
}; |
|||
|
|||
p.set = function(target, vars, position) { |
|||
position = this._parseTimeOrLabel(position, 0, true); |
|||
if (vars.immediateRender == null) { |
|||
vars.immediateRender = (position === this._time && !this._paused); |
|||
} |
|||
return this.add( new TweenLite(target, 0, vars), position); |
|||
}; |
|||
|
|||
TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) { |
|||
vars = vars || {}; |
|||
if (vars.smoothChildTiming == null) { |
|||
vars.smoothChildTiming = true; |
|||
} |
|||
var tl = new TimelineLite(vars), |
|||
root = tl._timeline, |
|||
tween, next; |
|||
if (ignoreDelayedCalls == null) { |
|||
ignoreDelayedCalls = true; |
|||
} |
|||
root._remove(tl, true); |
|||
tl._startTime = 0; |
|||
tl._rawPrevTime = tl._time = tl._totalTime = root._time; |
|||
tween = root._first; |
|||
while (tween) { |
|||
next = tween._next; |
|||
if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) { |
|||
tl.add(tween, tween._startTime - tween._delay); |
|||
} |
|||
tween = next; |
|||
} |
|||
root.add(tl, 0); |
|||
return tl; |
|||
}; |
|||
|
|||
p.add = function(value, position, align, stagger) { |
|||
var curTime, l, i, child, tl, beforeRawTime; |
|||
if (typeof(position) !== "number") { |
|||
position = this._parseTimeOrLabel(position, 0, true, value); |
|||
} |
|||
if (!(value instanceof Animation)) { |
|||
if ((value instanceof Array) || (value && value.push && _isArray(value))) { |
|||
align = align || "normal"; |
|||
stagger = stagger || 0; |
|||
curTime = position; |
|||
l = value.length; |
|||
for (i = 0; i < l; i++) { |
|||
if (_isArray(child = value[i])) { |
|||
child = new TimelineLite({tweens:child}); |
|||
} |
|||
this.add(child, curTime); |
|||
if (typeof(child) !== "string" && typeof(child) !== "function") { |
|||
if (align === "sequence") { |
|||
curTime = child._startTime + (child.totalDuration() / child._timeScale); |
|||
} else if (align === "start") { |
|||
child._startTime -= child.delay(); |
|||
} |
|||
} |
|||
curTime += stagger; |
|||
} |
|||
return this._uncache(true); |
|||
} else if (typeof(value) === "string") { |
|||
return this.addLabel(value, position); |
|||
} else if (typeof(value) === "function") { |
|||
value = TweenLite.delayedCall(0, value); |
|||
} else { |
|||
throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string."); |
|||
} |
|||
} |
|||
|
|||
SimpleTimeline.prototype.add.call(this, value, position); |
|||
|
|||
if (value._time) { //in case, for example, the _startTime is moved on a tween that has already rendered. Imagine it's at its end state, then the startTime is moved WAY later (after the end of this timeline), it should render at its beginning.
|
|||
value.render((this.rawTime() - value._startTime) * value._timeScale, false, false); |
|||
} |
|||
|
|||
//if the timeline has already ended but the inserted tween/timeline extends the duration, we should enable this timeline again so that it renders properly. We should also align the playhead with the parent timeline's when appropriate.
|
|||
if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) { |
|||
//in case any of the ancestors had completed but should now be enabled...
|
|||
tl = this; |
|||
beforeRawTime = (tl.rawTime() > value._startTime); //if the tween is placed on the timeline so that it starts BEFORE the current rawTime, we should align the playhead (move the timeline). This is because sometimes users will create a timeline, let it finish, and much later append a tween and expect it to run instead of jumping to its end state. While technically one could argue that it should jump to its end state, that's not what users intuitively expect.
|
|||
while (tl._timeline) { |
|||
if (beforeRawTime && tl._timeline.smoothChildTiming) { |
|||
tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it.
|
|||
} else if (tl._gc) { |
|||
tl._enabled(true, false); |
|||
} |
|||
tl = tl._timeline; |
|||
} |
|||
} |
|||
|
|||
return this; |
|||
}; |
|||
|
|||
p.remove = function(value) { |
|||
if (value instanceof Animation) { |
|||
this._remove(value, false); |
|||
var tl = value._timeline = value.vars.useFrames ? Animation._rootFramesTimeline : Animation._rootTimeline; //now that it's removed, default it to the root timeline so that if it gets played again, it doesn't jump back into this timeline.
|
|||
value._startTime = (value._paused ? value._pauseTime : tl._time) - ((!value._reversed ? value._totalTime : value.totalDuration() - value._totalTime) / value._timeScale); //ensure that if it gets played again, the timing is correct.
|
|||
return this; |
|||
} else if (value instanceof Array || (value && value.push && _isArray(value))) { |
|||
var i = value.length; |
|||
while (--i > -1) { |
|||
this.remove(value[i]); |
|||
} |
|||
return this; |
|||
} else if (typeof(value) === "string") { |
|||
return this.removeLabel(value); |
|||
} |
|||
return this.kill(null, value); |
|||
}; |
|||
|
|||
p._remove = function(tween, skipDisable) { |
|||
SimpleTimeline.prototype._remove.call(this, tween, skipDisable); |
|||
var last = this._last; |
|||
if (!last) { |
|||
this._time = this._totalTime = this._duration = this._totalDuration = 0; |
|||
} else if (this._time > this.duration()) { |
|||
this._time = this._duration; |
|||
this._totalTime = this._totalDuration; |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
p.append = function(value, offsetOrLabel) { |
|||
return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value)); |
|||
}; |
|||
|
|||
p.insert = p.insertMultiple = function(value, position, align, stagger) { |
|||
return this.add(value, position || 0, align, stagger); |
|||
}; |
|||
|
|||
p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) { |
|||
return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger); |
|||
}; |
|||
|
|||
p.addLabel = function(label, position) { |
|||
this._labels[label] = this._parseTimeOrLabel(position); |
|||
return this; |
|||
}; |
|||
|
|||
p.addPause = function(position, callback, params, scope) { |
|||
var t = TweenLite.delayedCall(0, _pauseCallback, params, scope || this); |
|||
t.vars.onComplete = t.vars.onReverseComplete = callback; |
|||
t.data = "isPause"; |
|||
this._hasPause = true; |
|||
return this.add(t, position); |
|||
}; |
|||
|
|||
p.removeLabel = function(label) { |
|||
delete this._labels[label]; |
|||
return this; |
|||
}; |
|||
|
|||
p.getLabelTime = function(label) { |
|||
return (this._labels[label] != null) ? this._labels[label] : -1; |
|||
}; |
|||
|
|||
p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) { |
|||
var clippedDuration, i; |
|||
//if we're about to add a tween/timeline (or an array of them) that's already a child of this timeline, we should remove it first so that it doesn't contaminate the duration().
|
|||
if (ignore instanceof Animation && ignore.timeline === this) { |
|||
this.remove(ignore); |
|||
} else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) { |
|||
i = ignore.length; |
|||
while (--i > -1) { |
|||
if (ignore[i] instanceof Animation && ignore[i].timeline === this) { |
|||
this.remove(ignore[i]); |
|||
} |
|||
} |
|||
} |
|||
clippedDuration = (this.duration() > 99999999999) ? this.recent().endTime(false) : this._duration; //in case there's a child that infinitely repeats, users almost never intend for the insertion point of a new child to be based on a SUPER long value like that so we clip it and assume the most recently-added child's endTime should be used instead.
|
|||
if (typeof(offsetOrLabel) === "string") { |
|||
return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - clippedDuration : 0, appendIfAbsent); |
|||
} |
|||
offsetOrLabel = offsetOrLabel || 0; |
|||
if (typeof(timeOrLabel) === "string" && (isNaN(timeOrLabel) || this._labels[timeOrLabel] != null)) { //if the string is a number like "1", check to see if there's a label with that name, otherwise interpret it as a number (absolute value).
|
|||
i = timeOrLabel.indexOf("="); |
|||
if (i === -1) { |
|||
if (this._labels[timeOrLabel] == null) { |
|||
return appendIfAbsent ? (this._labels[timeOrLabel] = clippedDuration + offsetOrLabel) : offsetOrLabel; |
|||
} |
|||
return this._labels[timeOrLabel] + offsetOrLabel; |
|||
} |
|||
offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1)); |
|||
timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : clippedDuration; |
|||
} else if (timeOrLabel == null) { |
|||
timeOrLabel = clippedDuration; |
|||
} |
|||
return Number(timeOrLabel) + offsetOrLabel; |
|||
}; |
|||
|
|||
p.seek = function(position, suppressEvents) { |
|||
return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false)); |
|||
}; |
|||
|
|||
p.stop = function() { |
|||
return this.paused(true); |
|||
}; |
|||
|
|||
p.gotoAndPlay = function(position, suppressEvents) { |
|||
return this.play(position, suppressEvents); |
|||
}; |
|||
|
|||
p.gotoAndStop = function(position, suppressEvents) { |
|||
return this.pause(position, suppressEvents); |
|||
}; |
|||
|
|||
p.render = function(time, suppressEvents, force) { |
|||
if (this._gc) { |
|||
this._enabled(true, false); |
|||
} |
|||
var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(), |
|||
prevTime = this._time, |
|||
prevStart = this._startTime, |
|||
prevTimeScale = this._timeScale, |
|||
prevPaused = this._paused, |
|||
tween, isComplete, next, callback, internalForce, pauseTween, curTime; |
|||
if (time >= totalDur - 0.0000001 && time >= 0) { //to work around occasional floating point math artifacts.
|
|||
this._totalTime = this._time = totalDur; |
|||
if (!this._reversed) if (!this._hasPausedChild()) { |
|||
isComplete = true; |
|||
callback = "onComplete"; |
|||
internalForce = !!this._timeline.autoRemoveChildren; //otherwise, if the animation is unpaused/activated after it's already finished, it doesn't get removed from the parent timeline.
|
|||
if (this._duration === 0) if ((time <= 0 && time >= -0.0000001) || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) { |
|||
internalForce = true; |
|||
if (this._rawPrevTime > _tinyNum) { |
|||
callback = "onReverseComplete"; |
|||
} |
|||
} |
|||
} |
|||
this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
|
|||
time = totalDur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7.
|
|||
|
|||
} else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
|
|||
this._totalTime = this._time = 0; |
|||
if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) { |
|||
callback = "onReverseComplete"; |
|||
isComplete = this._reversed; |
|||
} |
|||
if (time < 0) { |
|||
this._active = false; |
|||
if (this._timeline.autoRemoveChildren && this._reversed) { //ensures proper GC if a timeline is resumed after it's finished reversing.
|
|||
internalForce = isComplete = true; |
|||
callback = "onReverseComplete"; |
|||
} else if (this._rawPrevTime >= 0 && this._first) { //when going back beyond the start, force a render so that zero-duration tweens that sit at the very beginning render their start values properly. Otherwise, if the parent timeline's playhead lands exactly at this timeline's startTime, and then moves backwards, the zero-duration tweens at the beginning would still be at their end state.
|
|||
internalForce = true; |
|||
} |
|||
this._rawPrevTime = time; |
|||
} else { |
|||
this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
|
|||
if (time === 0 && isComplete) { //if there's a zero-duration tween at the very beginning of a timeline and the playhead lands EXACTLY at time 0, that tween will correctly render its end values, but we need to keep the timeline alive for one more render so that the beginning values render properly as the parent's playhead keeps moving beyond the begining. Imagine obj.x starts at 0 and then we do tl.set(obj, {x:100}).to(obj, 1, {x:200}) and then later we tl.reverse()...the goal is to have obj.x revert to 0. If the playhead happens to land on exactly 0, without this chunk of code, it'd complete the timeline and remove it from the rendering queue (not good).
|
|||
tween = this._first; |
|||
while (tween && tween._startTime === 0) { |
|||
if (!tween._duration) { |
|||
isComplete = false; |
|||
} |
|||
tween = tween._next; |
|||
} |
|||
} |
|||
time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
|
|||
if (!this._initted) { |
|||
internalForce = true; |
|||
} |
|||
} |
|||
|
|||
} else { |
|||
|
|||
if (this._hasPause && !this._forcingPlayhead && !suppressEvents) { |
|||
if (time >= prevTime) { |
|||
tween = this._first; |
|||
while (tween && tween._startTime <= time && !pauseTween) { |
|||
if (!tween._duration) if (tween.data === "isPause" && !tween.ratio && !(tween._startTime === 0 && this._rawPrevTime === 0)) { |
|||
pauseTween = tween; |
|||
} |
|||
tween = tween._next; |
|||
} |
|||
} else { |
|||
tween = this._last; |
|||
while (tween && tween._startTime >= time && !pauseTween) { |
|||
if (!tween._duration) if (tween.data === "isPause" && tween._rawPrevTime > 0) { |
|||
pauseTween = tween; |
|||
} |
|||
tween = tween._prev; |
|||
} |
|||
} |
|||
if (pauseTween) { |
|||
this._time = time = pauseTween._startTime; |
|||
this._totalTime = time + (this._cycle * (this._totalDuration + this._repeatDelay)); |
|||
} |
|||
} |
|||
|
|||
this._totalTime = this._time = this._rawPrevTime = time; |
|||
} |
|||
if ((this._time === prevTime || !this._first) && !force && !internalForce && !pauseTween) { |
|||
return; |
|||
} else if (!this._initted) { |
|||
this._initted = true; |
|||
} |
|||
|
|||
if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) { |
|||
this._active = true; //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example.
|
|||
} |
|||
|
|||
if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0 || !this._duration) if (!suppressEvents) { |
|||
this._callback("onStart"); |
|||
} |
|||
|
|||
curTime = this._time; |
|||
if (curTime >= prevTime) { |
|||
tween = this._first; |
|||
while (tween) { |
|||
next = tween._next; //record it here because the value could change after rendering...
|
|||
if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete
|
|||
break; |
|||
} else if (tween._active || (tween._startTime <= curTime && !tween._paused && !tween._gc)) { |
|||
if (pauseTween === tween) { |
|||
this.pause(); |
|||
} |
|||
if (!tween._reversed) { |
|||
tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); |
|||
} else { |
|||
tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); |
|||
} |
|||
} |
|||
tween = next; |
|||
} |
|||
} else { |
|||
tween = this._last; |
|||
while (tween) { |
|||
next = tween._prev; //record it here because the value could change after rendering...
|
|||
if (curTime !== this._time || (this._paused && !prevPaused)) { //in case a tween pauses or seeks the timeline when rendering, like inside of an onUpdate/onComplete
|
|||
break; |
|||
} else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) { |
|||
if (pauseTween === tween) { |
|||
pauseTween = tween._prev; //the linked list is organized by _startTime, thus it's possible that a tween could start BEFORE the pause and end after it, in which case it would be positioned before the pause tween in the linked list, but we should render it before we pause() the timeline and cease rendering. This is only a concern when going in reverse.
|
|||
while (pauseTween && pauseTween.endTime() > this._time) { |
|||
pauseTween.render( (pauseTween._reversed ? pauseTween.totalDuration() - ((time - pauseTween._startTime) * pauseTween._timeScale) : (time - pauseTween._startTime) * pauseTween._timeScale), suppressEvents, force); |
|||
pauseTween = pauseTween._prev; |
|||
} |
|||
pauseTween = null; |
|||
this.pause(); |
|||
} |
|||
if (!tween._reversed) { |
|||
tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force); |
|||
} else { |
|||
tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force); |
|||
} |
|||
} |
|||
tween = next; |
|||
} |
|||
} |
|||
|
|||
if (this._onUpdate) if (!suppressEvents) { |
|||
if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onUpdate on a timeline that reports/checks tweened values.
|
|||
_lazyRender(); |
|||
} |
|||
this._callback("onUpdate"); |
|||
} |
|||
|
|||
if (callback) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate
|
|||
if (isComplete) { |
|||
if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when a timeline finishes, users expect things to have rendered fully. Imagine an onComplete on a timeline that reports/checks tweened values.
|
|||
_lazyRender(); |
|||
} |
|||
if (this._timeline.autoRemoveChildren) { |
|||
this._enabled(false, false); |
|||
} |
|||
this._active = false; |
|||
} |
|||
if (!suppressEvents && this.vars[callback]) { |
|||
this._callback(callback); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
p._hasPausedChild = function() { |
|||
var tween = this._first; |
|||
while (tween) { |
|||
if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) { |
|||
return true; |
|||
} |
|||
tween = tween._next; |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) { |
|||
ignoreBeforeTime = ignoreBeforeTime || -9999999999; |
|||
var a = [], |
|||
tween = this._first, |
|||
cnt = 0; |
|||
while (tween) { |
|||
if (tween._startTime < ignoreBeforeTime) { |
|||
//do nothing
|
|||
} else if (tween instanceof TweenLite) { |
|||
if (tweens !== false) { |
|||
a[cnt++] = tween; |
|||
} |
|||
} else { |
|||
if (timelines !== false) { |
|||
a[cnt++] = tween; |
|||
} |
|||
if (nested !== false) { |
|||
a = a.concat(tween.getChildren(true, tweens, timelines)); |
|||
cnt = a.length; |
|||
} |
|||
} |
|||
tween = tween._next; |
|||
} |
|||
return a; |
|||
}; |
|||
|
|||
p.getTweensOf = function(target, nested) { |
|||
var disabled = this._gc, |
|||
a = [], |
|||
cnt = 0, |
|||
tweens, i; |
|||
if (disabled) { |
|||
this._enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here.
|
|||
} |
|||
tweens = TweenLite.getTweensOf(target); |
|||
i = tweens.length; |
|||
while (--i > -1) { |
|||
if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) { |
|||
a[cnt++] = tweens[i]; |
|||
} |
|||
} |
|||
if (disabled) { |
|||
this._enabled(false, true); |
|||
} |
|||
return a; |
|||
}; |
|||
|
|||
p.recent = function() { |
|||
return this._recent; |
|||
}; |
|||
|
|||
p._contains = function(tween) { |
|||
var tl = tween.timeline; |
|||
while (tl) { |
|||
if (tl === this) { |
|||
return true; |
|||
} |
|||
tl = tl.timeline; |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) { |
|||
ignoreBeforeTime = ignoreBeforeTime || 0; |
|||
var tween = this._first, |
|||
labels = this._labels, |
|||
p; |
|||
while (tween) { |
|||
if (tween._startTime >= ignoreBeforeTime) { |
|||
tween._startTime += amount; |
|||
} |
|||
tween = tween._next; |
|||
} |
|||
if (adjustLabels) { |
|||
for (p in labels) { |
|||
if (labels[p] >= ignoreBeforeTime) { |
|||
labels[p] += amount; |
|||
} |
|||
} |
|||
} |
|||
return this._uncache(true); |
|||
}; |
|||
|
|||
p._kill = function(vars, target) { |
|||
if (!vars && !target) { |
|||
return this._enabled(false, false); |
|||
} |
|||
var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target), |
|||
i = tweens.length, |
|||
changed = false; |
|||
while (--i > -1) { |
|||
if (tweens[i]._kill(vars, target)) { |
|||
changed = true; |
|||
} |
|||
} |
|||
return changed; |
|||
}; |
|||
|
|||
p.clear = function(labels) { |
|||
var tweens = this.getChildren(false, true, true), |
|||
i = tweens.length; |
|||
this._time = this._totalTime = 0; |
|||
while (--i > -1) { |
|||
tweens[i]._enabled(false, false); |
|||
} |
|||
if (labels !== false) { |
|||
this._labels = {}; |
|||
} |
|||
return this._uncache(true); |
|||
}; |
|||
|
|||
p.invalidate = function() { |
|||
var tween = this._first; |
|||
while (tween) { |
|||
tween.invalidate(); |
|||
tween = tween._next; |
|||
} |
|||
return Animation.prototype.invalidate.call(this);; |
|||
}; |
|||
|
|||
p._enabled = function(enabled, ignoreTimeline) { |
|||
if (enabled === this._gc) { |
|||
var tween = this._first; |
|||
while (tween) { |
|||
tween._enabled(enabled, true); |
|||
tween = tween._next; |
|||
} |
|||
} |
|||
return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline); |
|||
}; |
|||
|
|||
p.totalTime = function(time, suppressEvents, uncapped) { |
|||
this._forcingPlayhead = true; |
|||
var val = Animation.prototype.totalTime.apply(this, arguments); |
|||
this._forcingPlayhead = false; |
|||
return val; |
|||
}; |
|||
|
|||
p.duration = function(value) { |
|||
if (!arguments.length) { |
|||
if (this._dirty) { |
|||
this.totalDuration(); //just triggers recalculation
|
|||
} |
|||
return this._duration; |
|||
} |
|||
if (this.duration() !== 0 && value !== 0) { |
|||
this.timeScale(this._duration / value); |
|||
} |
|||
return this; |
|||
}; |
|||
|
|||
p.totalDuration = function(value) { |
|||
if (!arguments.length) { |
|||
if (this._dirty) { |
|||
var max = 0, |
|||
tween = this._last, |
|||
prevStart = 999999999999, |
|||
prev, end; |
|||
while (tween) { |
|||
prev = tween._prev; //record it here in case the tween changes position in the sequence...
|
|||
if (tween._dirty) { |
|||
tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it.
|
|||
} |
|||
if (tween._startTime > prevStart && this._sortChildren && !tween._paused) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence
|
|||
this.add(tween, tween._startTime - tween._delay); |
|||
} else { |
|||
prevStart = tween._startTime; |
|||
} |
|||
if (tween._startTime < 0 && !tween._paused) { //children aren't allowed to have negative startTimes unless smoothChildTiming is true, so adjust here if one is found.
|
|||
max -= tween._startTime; |
|||
if (this._timeline.smoothChildTiming) { |
|||
this._startTime += tween._startTime / this._timeScale; |
|||
} |
|||
this.shiftChildren(-tween._startTime, false, -9999999999); |
|||
prevStart = 0; |
|||
} |
|||
end = tween._startTime + (tween._totalDuration / tween._timeScale); |
|||
if (end > max) { |
|||
max = end; |
|||
} |
|||
tween = prev; |
|||
} |
|||
this._duration = this._totalDuration = max; |
|||
this._dirty = false; |
|||
} |
|||
return this._totalDuration; |
|||
} |
|||
return (value && this.totalDuration()) ? this.timeScale(this._totalDuration / value) : this; |
|||
}; |
|||
|
|||
p.paused = function(value) { |
|||
if (!value) { //if there's a pause directly at the spot from where we're unpausing, skip it.
|
|||
var tween = this._first, |
|||
time = this._time; |
|||
while (tween) { |
|||
if (tween._startTime === time && tween.data === "isPause") { |
|||
tween._rawPrevTime = 0; //remember, _rawPrevTime is how zero-duration tweens/callbacks sense directionality and determine whether or not to fire. If _rawPrevTime is the same as _startTime on the next render, it won't fire.
|
|||
} |
|||
tween = tween._next; |
|||
} |
|||
} |
|||
return Animation.prototype.paused.apply(this, arguments); |
|||
}; |
|||
|
|||
p.usesFrames = function() { |
|||
var tl = this._timeline; |
|||
while (tl._timeline) { |
|||
tl = tl._timeline; |
|||
} |
|||
return (tl === Animation._rootFramesTimeline); |
|||
}; |
|||
|
|||
p.rawTime = function(wrapRepeats) { |
|||
return (wrapRepeats && (this._paused || (this._repeat && this.time() > 0 && this.totalProgress() < 1))) ? this._totalTime % (this._duration + this._repeatDelay) : this._paused ? this._totalTime : (this._timeline.rawTime(wrapRepeats) - this._startTime) * this._timeScale; |
|||
}; |
|||
|
|||
return TimelineLite; |
|||
|
|||
}, true); |
|||
|
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("./TweenLite.js"); //dependency
|
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("TimelineLite")); |
|||
@ -0,0 +1,360 @@ |
|||
/*! |
|||
* VERSION: 1.15.6 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine("easing.Back", ["easing.Ease"], function(Ease) { |
|||
|
|||
var w = (_gsScope.GreenSockGlobals || _gsScope), |
|||
gs = w.com.greensock, |
|||
_2PI = Math.PI * 2, |
|||
_HALF_PI = Math.PI / 2, |
|||
_class = gs._class, |
|||
_create = function(n, f) { |
|||
var C = _class("easing." + n, function(){}, true), |
|||
p = C.prototype = new Ease(); |
|||
p.constructor = C; |
|||
p.getRatio = f; |
|||
return C; |
|||
}, |
|||
_easeReg = Ease.register || function(){}, //put an empty function in place just as a safety measure in case someone loads an OLD version of TweenLite.js where Ease.register doesn't exist.
|
|||
_wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) { |
|||
var C = _class("easing."+name, { |
|||
easeOut:new EaseOut(), |
|||
easeIn:new EaseIn(), |
|||
easeInOut:new EaseInOut() |
|||
}, true); |
|||
_easeReg(C, name); |
|||
return C; |
|||
}, |
|||
EasePoint = function(time, value, next) { |
|||
this.t = time; |
|||
this.v = value; |
|||
if (next) { |
|||
this.next = next; |
|||
next.prev = this; |
|||
this.c = next.v - value; |
|||
this.gap = next.t - time; |
|||
} |
|||
}, |
|||
|
|||
//Back
|
|||
_createBack = function(n, f) { |
|||
var C = _class("easing." + n, function(overshoot) { |
|||
this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158; |
|||
this._p2 = this._p1 * 1.525; |
|||
}, true), |
|||
p = C.prototype = new Ease(); |
|||
p.constructor = C; |
|||
p.getRatio = f; |
|||
p.config = function(overshoot) { |
|||
return new C(overshoot); |
|||
}; |
|||
return C; |
|||
}, |
|||
|
|||
Back = _wrap("Back", |
|||
_createBack("BackOut", function(p) { |
|||
return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1); |
|||
}), |
|||
_createBack("BackIn", function(p) { |
|||
return p * p * ((this._p1 + 1) * p - this._p1); |
|||
}), |
|||
_createBack("BackInOut", function(p) { |
|||
return ((p *= 2) < 1) ? 0.5 * p * p * ((this._p2 + 1) * p - this._p2) : 0.5 * ((p -= 2) * p * ((this._p2 + 1) * p + this._p2) + 2); |
|||
}) |
|||
), |
|||
|
|||
|
|||
//SlowMo
|
|||
SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) { |
|||
power = (power || power === 0) ? power : 0.7; |
|||
if (linearRatio == null) { |
|||
linearRatio = 0.7; |
|||
} else if (linearRatio > 1) { |
|||
linearRatio = 1; |
|||
} |
|||
this._p = (linearRatio !== 1) ? power : 0; |
|||
this._p1 = (1 - linearRatio) / 2; |
|||
this._p2 = linearRatio; |
|||
this._p3 = this._p1 + this._p2; |
|||
this._calcEnd = (yoyoMode === true); |
|||
}, true), |
|||
p = SlowMo.prototype = new Ease(), |
|||
SteppedEase, RoughEase, _createElastic; |
|||
|
|||
p.constructor = SlowMo; |
|||
p.getRatio = function(p) { |
|||
var r = p + (0.5 - p) * this._p; |
|||
if (p < this._p1) { |
|||
return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r); |
|||
} else if (p > this._p3) { |
|||
return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p); |
|||
} |
|||
return this._calcEnd ? 1 : r; |
|||
}; |
|||
SlowMo.ease = new SlowMo(0.7, 0.7); |
|||
|
|||
p.config = SlowMo.config = function(linearRatio, power, yoyoMode) { |
|||
return new SlowMo(linearRatio, power, yoyoMode); |
|||
}; |
|||
|
|||
|
|||
//SteppedEase
|
|||
SteppedEase = _class("easing.SteppedEase", function(steps, immediateStart) { |
|||
steps = steps || 1; |
|||
this._p1 = 1 / steps; |
|||
this._p2 = steps + (immediateStart ? 0 : 1); |
|||
this._p3 = immediateStart ? 1 : 0; |
|||
}, true); |
|||
p = SteppedEase.prototype = new Ease(); |
|||
p.constructor = SteppedEase; |
|||
p.getRatio = function(p) { |
|||
if (p < 0) { |
|||
p = 0; |
|||
} else if (p >= 1) { |
|||
p = 0.999999999; |
|||
} |
|||
return (((this._p2 * p) | 0) + this._p3) * this._p1; |
|||
}; |
|||
p.config = SteppedEase.config = function(steps, immediateStart) { |
|||
return new SteppedEase(steps, immediateStart); |
|||
}; |
|||
|
|||
|
|||
//RoughEase
|
|||
RoughEase = _class("easing.RoughEase", function(vars) { |
|||
vars = vars || {}; |
|||
var taper = vars.taper || "none", |
|||
a = [], |
|||
cnt = 0, |
|||
points = (vars.points || 20) | 0, |
|||
i = points, |
|||
randomize = (vars.randomize !== false), |
|||
clamp = (vars.clamp === true), |
|||
template = (vars.template instanceof Ease) ? vars.template : null, |
|||
strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4, |
|||
x, y, bump, invX, obj, pnt; |
|||
while (--i > -1) { |
|||
x = randomize ? Math.random() : (1 / points) * i; |
|||
y = template ? template.getRatio(x) : x; |
|||
if (taper === "none") { |
|||
bump = strength; |
|||
} else if (taper === "out") { |
|||
invX = 1 - x; |
|||
bump = invX * invX * strength; |
|||
} else if (taper === "in") { |
|||
bump = x * x * strength; |
|||
} else if (x < 0.5) { //"both" (start)
|
|||
invX = x * 2; |
|||
bump = invX * invX * 0.5 * strength; |
|||
} else { //"both" (end)
|
|||
invX = (1 - x) * 2; |
|||
bump = invX * invX * 0.5 * strength; |
|||
} |
|||
if (randomize) { |
|||
y += (Math.random() * bump) - (bump * 0.5); |
|||
} else if (i % 2) { |
|||
y += bump * 0.5; |
|||
} else { |
|||
y -= bump * 0.5; |
|||
} |
|||
if (clamp) { |
|||
if (y > 1) { |
|||
y = 1; |
|||
} else if (y < 0) { |
|||
y = 0; |
|||
} |
|||
} |
|||
a[cnt++] = {x:x, y:y}; |
|||
} |
|||
a.sort(function(a, b) { |
|||
return a.x - b.x; |
|||
}); |
|||
|
|||
pnt = new EasePoint(1, 1, null); |
|||
i = points; |
|||
while (--i > -1) { |
|||
obj = a[i]; |
|||
pnt = new EasePoint(obj.x, obj.y, pnt); |
|||
} |
|||
|
|||
this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next); |
|||
}, true); |
|||
p = RoughEase.prototype = new Ease(); |
|||
p.constructor = RoughEase; |
|||
p.getRatio = function(p) { |
|||
var pnt = this._prev; |
|||
if (p > pnt.t) { |
|||
while (pnt.next && p >= pnt.t) { |
|||
pnt = pnt.next; |
|||
} |
|||
pnt = pnt.prev; |
|||
} else { |
|||
while (pnt.prev && p <= pnt.t) { |
|||
pnt = pnt.prev; |
|||
} |
|||
} |
|||
this._prev = pnt; |
|||
return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c); |
|||
}; |
|||
p.config = function(vars) { |
|||
return new RoughEase(vars); |
|||
}; |
|||
RoughEase.ease = new RoughEase(); |
|||
|
|||
|
|||
//Bounce
|
|||
_wrap("Bounce", |
|||
_create("BounceOut", function(p) { |
|||
if (p < 1 / 2.75) { |
|||
return 7.5625 * p * p; |
|||
} else if (p < 2 / 2.75) { |
|||
return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; |
|||
} else if (p < 2.5 / 2.75) { |
|||
return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; |
|||
} |
|||
return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; |
|||
}), |
|||
_create("BounceIn", function(p) { |
|||
if ((p = 1 - p) < 1 / 2.75) { |
|||
return 1 - (7.5625 * p * p); |
|||
} else if (p < 2 / 2.75) { |
|||
return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75); |
|||
} else if (p < 2.5 / 2.75) { |
|||
return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375); |
|||
} |
|||
return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375); |
|||
}), |
|||
_create("BounceInOut", function(p) { |
|||
var invert = (p < 0.5); |
|||
if (invert) { |
|||
p = 1 - (p * 2); |
|||
} else { |
|||
p = (p * 2) - 1; |
|||
} |
|||
if (p < 1 / 2.75) { |
|||
p = 7.5625 * p * p; |
|||
} else if (p < 2 / 2.75) { |
|||
p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75; |
|||
} else if (p < 2.5 / 2.75) { |
|||
p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375; |
|||
} else { |
|||
p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375; |
|||
} |
|||
return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5; |
|||
}) |
|||
); |
|||
|
|||
|
|||
//CIRC
|
|||
_wrap("Circ", |
|||
_create("CircOut", function(p) { |
|||
return Math.sqrt(1 - (p = p - 1) * p); |
|||
}), |
|||
_create("CircIn", function(p) { |
|||
return -(Math.sqrt(1 - (p * p)) - 1); |
|||
}), |
|||
_create("CircInOut", function(p) { |
|||
return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1); |
|||
}) |
|||
); |
|||
|
|||
|
|||
//Elastic
|
|||
_createElastic = function(n, f, def) { |
|||
var C = _class("easing." + n, function(amplitude, period) { |
|||
this._p1 = (amplitude >= 1) ? amplitude : 1; //note: if amplitude is < 1, we simply adjust the period for a more natural feel. Otherwise the math doesn't work right and the curve starts at 1.
|
|||
this._p2 = (period || def) / (amplitude < 1 ? amplitude : 1); |
|||
this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0); |
|||
this._p2 = _2PI / this._p2; //precalculate to optimize
|
|||
}, true), |
|||
p = C.prototype = new Ease(); |
|||
p.constructor = C; |
|||
p.getRatio = f; |
|||
p.config = function(amplitude, period) { |
|||
return new C(amplitude, period); |
|||
}; |
|||
return C; |
|||
}; |
|||
_wrap("Elastic", |
|||
_createElastic("ElasticOut", function(p) { |
|||
return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * this._p2 ) + 1; |
|||
}, 0.3), |
|||
_createElastic("ElasticIn", function(p) { |
|||
return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * this._p2 )); |
|||
}, 0.3), |
|||
_createElastic("ElasticInOut", function(p) { |
|||
return ((p *= 2) < 1) ? -0.5 * (this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * this._p2)) : this._p1 * Math.pow(2, -10 *(p -= 1)) * Math.sin( (p - this._p3) * this._p2 ) * 0.5 + 1; |
|||
}, 0.45) |
|||
); |
|||
|
|||
|
|||
//Expo
|
|||
_wrap("Expo", |
|||
_create("ExpoOut", function(p) { |
|||
return 1 - Math.pow(2, -10 * p); |
|||
}), |
|||
_create("ExpoIn", function(p) { |
|||
return Math.pow(2, 10 * (p - 1)) - 0.001; |
|||
}), |
|||
_create("ExpoInOut", function(p) { |
|||
return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1))); |
|||
}) |
|||
); |
|||
|
|||
|
|||
//Sine
|
|||
_wrap("Sine", |
|||
_create("SineOut", function(p) { |
|||
return Math.sin(p * _HALF_PI); |
|||
}), |
|||
_create("SineIn", function(p) { |
|||
return -Math.cos(p * _HALF_PI) + 1; |
|||
}), |
|||
_create("SineInOut", function(p) { |
|||
return -0.5 * (Math.cos(Math.PI * p) - 1); |
|||
}) |
|||
); |
|||
|
|||
_class("easing.EaseLookup", { |
|||
find:function(s) { |
|||
return Ease.map[s]; |
|||
} |
|||
}, true); |
|||
|
|||
//register the non-standard eases
|
|||
_easeReg(w.SlowMo, "SlowMo", "ease,"); |
|||
_easeReg(RoughEase, "RoughEase", "ease,"); |
|||
_easeReg(SteppedEase, "SteppedEase", "ease,"); |
|||
|
|||
return Back; |
|||
|
|||
}, true); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function() { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope); |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}()); |
|||
@ -0,0 +1,55 @@ |
|||
/*! |
|||
* VERSION: 0.6.1 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine.plugin({ |
|||
propName: "attr", |
|||
API: 2, |
|||
version: "0.6.1", |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween, index) { |
|||
var p, end; |
|||
if (typeof(target.setAttribute) !== "function") { |
|||
return false; |
|||
} |
|||
for (p in value) { |
|||
end = value[p]; |
|||
if (typeof(end) === "function") { |
|||
end = end(index, target); |
|||
} |
|||
this._addTween(target, "setAttribute", target.getAttribute(p) + "", end + "", p, false, p); |
|||
this._overwriteProps.push(p); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
}); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("AttrPlugin")); |
|||
@ -0,0 +1,620 @@ |
|||
/*! |
|||
* VERSION: 1.3.8 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _RAD2DEG = 180 / Math.PI, |
|||
_r1 = [], |
|||
_r2 = [], |
|||
_r3 = [], |
|||
_corProps = {}, |
|||
_globals = _gsScope._gsDefine.globals, |
|||
Segment = function(a, b, c, d) { |
|||
if (c === d) { //if c and d match, the final autoRotate value could lock at -90 degrees, so differentiate them slightly.
|
|||
c = d - (d - b) / 1000000; |
|||
} |
|||
if (a === b) { //if a and b match, the starting autoRotate value could lock at -90 degrees, so differentiate them slightly.
|
|||
b = a + (c - a) / 1000000; |
|||
} |
|||
this.a = a; |
|||
this.b = b; |
|||
this.c = c; |
|||
this.d = d; |
|||
this.da = d - a; |
|||
this.ca = c - a; |
|||
this.ba = b - a; |
|||
}, |
|||
_correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,", |
|||
cubicToQuadratic = function(a, b, c, d) { |
|||
var q1 = {a:a}, |
|||
q2 = {}, |
|||
q3 = {}, |
|||
q4 = {c:d}, |
|||
mab = (a + b) / 2, |
|||
mbc = (b + c) / 2, |
|||
mcd = (c + d) / 2, |
|||
mabc = (mab + mbc) / 2, |
|||
mbcd = (mbc + mcd) / 2, |
|||
m8 = (mbcd - mabc) / 8; |
|||
q1.b = mab + (a - mab) / 4; |
|||
q2.b = mabc + m8; |
|||
q1.c = q2.a = (q1.b + q2.b) / 2; |
|||
q2.c = q3.a = (mabc + mbcd) / 2; |
|||
q3.b = mbcd - m8; |
|||
q4.b = mcd + (d - mcd) / 4; |
|||
q3.c = q4.a = (q3.b + q4.b) / 2; |
|||
return [q1, q2, q3, q4]; |
|||
}, |
|||
_calculateControlPoints = function(a, curviness, quad, basic, correlate) { |
|||
var l = a.length - 1, |
|||
ii = 0, |
|||
cp1 = a[0].a, |
|||
i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl; |
|||
for (i = 0; i < l; i++) { |
|||
seg = a[ii]; |
|||
p1 = seg.a; |
|||
p2 = seg.d; |
|||
p3 = a[ii+1].d; |
|||
|
|||
if (correlate) { |
|||
r1 = _r1[i]; |
|||
r2 = _r2[i]; |
|||
tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5); |
|||
m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0)); |
|||
m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0)); |
|||
mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0)); |
|||
} else { |
|||
m1 = p2 - (p2 - p1) * curviness * 0.5; |
|||
m2 = p2 + (p3 - p2) * curviness * 0.5; |
|||
mm = p2 - (m1 + m2) / 2; |
|||
} |
|||
m1 += mm; |
|||
m2 += mm; |
|||
|
|||
seg.c = cp2 = m1; |
|||
if (i !== 0) { |
|||
seg.b = cp1; |
|||
} else { |
|||
seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly.
|
|||
} |
|||
|
|||
seg.da = p2 - p1; |
|||
seg.ca = cp2 - p1; |
|||
seg.ba = cp1 - p1; |
|||
|
|||
if (quad) { |
|||
qb = cubicToQuadratic(p1, cp1, cp2, p2); |
|||
a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); |
|||
ii += 4; |
|||
} else { |
|||
ii++; |
|||
} |
|||
|
|||
cp1 = m2; |
|||
} |
|||
seg = a[ii]; |
|||
seg.b = cp1; |
|||
seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly.
|
|||
seg.da = seg.d - seg.a; |
|||
seg.ca = seg.c - seg.a; |
|||
seg.ba = cp1 - seg.a; |
|||
if (quad) { |
|||
qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d); |
|||
a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]); |
|||
} |
|||
}, |
|||
_parseAnchors = function(values, p, correlate, prepend) { |
|||
var a = [], |
|||
l, i, p1, p2, p3, tmp; |
|||
if (prepend) { |
|||
values = [prepend].concat(values); |
|||
i = values.length; |
|||
while (--i > -1) { |
|||
if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") { |
|||
values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons
|
|||
} |
|||
} |
|||
} |
|||
l = values.length - 2; |
|||
if (l < 0) { |
|||
a[0] = new Segment(values[0][p], 0, 0, values[0][p]); |
|||
return a; |
|||
} |
|||
for (i = 0; i < l; i++) { |
|||
p1 = values[i][p]; |
|||
p2 = values[i+1][p]; |
|||
a[i] = new Segment(p1, 0, 0, p2); |
|||
if (correlate) { |
|||
p3 = values[i+2][p]; |
|||
_r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1); |
|||
_r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2); |
|||
} |
|||
} |
|||
a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]); |
|||
return a; |
|||
}, |
|||
bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) { |
|||
var obj = {}, |
|||
props = [], |
|||
first = prepend || values[0], |
|||
i, p, a, j, r, l, seamless, last; |
|||
correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate; |
|||
if (curviness == null) { |
|||
curviness = 1; |
|||
} |
|||
for (p in values[0]) { |
|||
props.push(p); |
|||
} |
|||
//check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later)
|
|||
if (values.length > 1) { |
|||
last = values[values.length - 1]; |
|||
seamless = true; |
|||
i = props.length; |
|||
while (--i > -1) { |
|||
p = props[i]; |
|||
if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors.
|
|||
seamless = false; |
|||
break; |
|||
} |
|||
} |
|||
if (seamless) { |
|||
values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
|
|||
if (prepend) { |
|||
values.unshift(prepend); |
|||
} |
|||
values.push(values[1]); |
|||
prepend = values[values.length - 3]; |
|||
} |
|||
} |
|||
_r1.length = _r2.length = _r3.length = 0; |
|||
i = props.length; |
|||
while (--i > -1) { |
|||
p = props[i]; |
|||
_corProps[p] = (correlate.indexOf(","+p+",") !== -1); |
|||
obj[p] = _parseAnchors(values, p, _corProps[p], prepend); |
|||
} |
|||
i = _r1.length; |
|||
while (--i > -1) { |
|||
_r1[i] = Math.sqrt(_r1[i]); |
|||
_r2[i] = Math.sqrt(_r2[i]); |
|||
} |
|||
if (!basic) { |
|||
i = props.length; |
|||
while (--i > -1) { |
|||
if (_corProps[p]) { |
|||
a = obj[props[i]]; |
|||
l = a.length - 1; |
|||
for (j = 0; j < l; j++) { |
|||
r = (a[j+1].da / _r2[j] + a[j].da / _r1[j]) || 0; |
|||
_r3[j] = (_r3[j] || 0) + r * r; |
|||
} |
|||
} |
|||
} |
|||
i = _r3.length; |
|||
while (--i > -1) { |
|||
_r3[i] = Math.sqrt(_r3[i]); |
|||
} |
|||
} |
|||
i = props.length; |
|||
j = quadratic ? 4 : 1; |
|||
while (--i > -1) { |
|||
p = props[i]; |
|||
a = obj[p]; |
|||
_calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties
|
|||
if (seamless) { |
|||
a.splice(0, j); |
|||
a.splice(a.length - j, j); |
|||
} |
|||
} |
|||
return obj; |
|||
}, |
|||
_parseBezierData = function(values, type, prepend) { |
|||
type = type || "soft"; |
|||
var obj = {}, |
|||
inc = (type === "cubic") ? 3 : 2, |
|||
soft = (type === "soft"), |
|||
props = [], |
|||
a, b, c, d, cur, i, j, l, p, cnt, tmp; |
|||
if (soft && prepend) { |
|||
values = [prepend].concat(values); |
|||
} |
|||
if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; } |
|||
for (p in values[0]) { |
|||
props.push(p); |
|||
} |
|||
i = props.length; |
|||
while (--i > -1) { |
|||
p = props[i]; |
|||
obj[p] = cur = []; |
|||
cnt = 0; |
|||
l = values.length; |
|||
for (j = 0; j < l; j++) { |
|||
a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp); |
|||
if (soft) if (j > 1) if (j < l - 1) { |
|||
cur[cnt++] = (a + cur[cnt-2]) / 2; |
|||
} |
|||
cur[cnt++] = a; |
|||
} |
|||
l = cnt - inc + 1; |
|||
cnt = 0; |
|||
for (j = 0; j < l; j += inc) { |
|||
a = cur[j]; |
|||
b = cur[j+1]; |
|||
c = cur[j+2]; |
|||
d = (inc === 2) ? 0 : cur[j+3]; |
|||
cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); |
|||
} |
|||
cur.length = cnt; |
|||
} |
|||
return obj; |
|||
}, |
|||
_addCubicLengths = function(a, steps, resolution) { |
|||
var inc = 1 / resolution, |
|||
j = a.length, |
|||
d, d1, s, da, ca, ba, p, i, inv, bez, index; |
|||
while (--j > -1) { |
|||
bez = a[j]; |
|||
s = bez.a; |
|||
da = bez.d - s; |
|||
ca = bez.c - s; |
|||
ba = bez.b - s; |
|||
d = d1 = 0; |
|||
for (i = 1; i <= resolution; i++) { |
|||
p = inc * i; |
|||
inv = 1 - p; |
|||
d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p); |
|||
index = j * resolution + i - 1; |
|||
steps[index] = (steps[index] || 0) + d * d; |
|||
} |
|||
} |
|||
}, |
|||
_parseLengthData = function(obj, resolution) { |
|||
resolution = resolution >> 0 || 6; |
|||
var a = [], |
|||
lengths = [], |
|||
d = 0, |
|||
total = 0, |
|||
threshold = resolution - 1, |
|||
segments = [], |
|||
curLS = [], //current length segments array
|
|||
p, i, l, index; |
|||
for (p in obj) { |
|||
_addCubicLengths(obj[p], a, resolution); |
|||
} |
|||
l = a.length; |
|||
for (i = 0; i < l; i++) { |
|||
d += Math.sqrt(a[i]); |
|||
index = i % resolution; |
|||
curLS[index] = d; |
|||
if (index === threshold) { |
|||
total += d; |
|||
index = (i / resolution) >> 0; |
|||
segments[index] = curLS; |
|||
lengths[index] = total; |
|||
d = 0; |
|||
curLS = []; |
|||
} |
|||
} |
|||
return {length:total, lengths:lengths, segments:segments}; |
|||
}, |
|||
|
|||
|
|||
|
|||
BezierPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "bezier", |
|||
priority: -1, |
|||
version: "1.3.8", |
|||
API: 2, |
|||
global:true, |
|||
|
|||
//gets called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, vars, tween) { |
|||
this._target = target; |
|||
if (vars instanceof Array) { |
|||
vars = {values:vars}; |
|||
} |
|||
this._func = {}; |
|||
this._mod = {}; |
|||
this._props = []; |
|||
this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10); |
|||
var values = vars.values || [], |
|||
first = {}, |
|||
second = values[0], |
|||
autoRotate = vars.autoRotate || tween.vars.orientToBezier, |
|||
p, isFunc, i, j, prepend; |
|||
|
|||
this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null; |
|||
for (p in second) { |
|||
this._props.push(p); |
|||
} |
|||
|
|||
i = this._props.length; |
|||
while (--i > -1) { |
|||
p = this._props[i]; |
|||
|
|||
this._overwriteProps.push(p); |
|||
isFunc = this._func[p] = (typeof(target[p]) === "function"); |
|||
first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ](); |
|||
if (!prepend) if (first[p] !== values[0][p]) { |
|||
prepend = first; |
|||
} |
|||
} |
|||
this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate, prepend) : _parseBezierData(values, vars.type, first); |
|||
this._segCount = this._beziers[p].length; |
|||
|
|||
if (this._timeRes) { |
|||
var ld = _parseLengthData(this._beziers, this._timeRes); |
|||
this._length = ld.length; |
|||
this._lengths = ld.lengths; |
|||
this._segments = ld.segments; |
|||
this._l1 = this._li = this._s1 = this._si = 0; |
|||
this._l2 = this._lengths[0]; |
|||
this._curSeg = this._segments[0]; |
|||
this._s2 = this._curSeg[0]; |
|||
this._prec = 1 / this._curSeg.length; |
|||
} |
|||
|
|||
if ((autoRotate = this._autoRotate)) { |
|||
this._initialRotations = []; |
|||
if (!(autoRotate[0] instanceof Array)) { |
|||
this._autoRotate = autoRotate = [autoRotate]; |
|||
} |
|||
i = autoRotate.length; |
|||
while (--i > -1) { |
|||
for (j = 0; j < 3; j++) { |
|||
p = autoRotate[i][j]; |
|||
this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false; |
|||
} |
|||
p = autoRotate[i][2]; |
|||
this._initialRotations[i] = (this._func[p] ? this._func[p].call(this._target) : this._target[p]) || 0; |
|||
this._overwriteProps.push(p); |
|||
} |
|||
} |
|||
this._startRatio = tween.vars.runBackwards ? 1 : 0; //we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (indicating it's a from() tween) in which case it's 1.
|
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(v) { |
|||
var segments = this._segCount, |
|||
func = this._func, |
|||
target = this._target, |
|||
notStart = (v !== this._startRatio), |
|||
curIndex, inv, i, p, b, t, val, l, lengths, curSeg; |
|||
if (!this._timeRes) { |
|||
curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0; |
|||
t = (v - (curIndex * (1 / segments))) * segments; |
|||
} else { |
|||
lengths = this._lengths; |
|||
curSeg = this._curSeg; |
|||
v *= this._length; |
|||
i = this._li; |
|||
//find the appropriate segment (if the currently cached one isn't correct)
|
|||
if (v > this._l2 && i < segments - 1) { |
|||
l = segments - 1; |
|||
while (i < l && (this._l2 = lengths[++i]) <= v) { } |
|||
this._l1 = lengths[i-1]; |
|||
this._li = i; |
|||
this._curSeg = curSeg = this._segments[i]; |
|||
this._s2 = curSeg[(this._s1 = this._si = 0)]; |
|||
} else if (v < this._l1 && i > 0) { |
|||
while (i > 0 && (this._l1 = lengths[--i]) >= v) { } |
|||
if (i === 0 && v < this._l1) { |
|||
this._l1 = 0; |
|||
} else { |
|||
i++; |
|||
} |
|||
this._l2 = lengths[i]; |
|||
this._li = i; |
|||
this._curSeg = curSeg = this._segments[i]; |
|||
this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0; |
|||
this._s2 = curSeg[this._si]; |
|||
} |
|||
curIndex = i; |
|||
//now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
|
|||
v -= this._l1; |
|||
i = this._si; |
|||
if (v > this._s2 && i < curSeg.length - 1) { |
|||
l = curSeg.length - 1; |
|||
while (i < l && (this._s2 = curSeg[++i]) <= v) { } |
|||
this._s1 = curSeg[i-1]; |
|||
this._si = i; |
|||
} else if (v < this._s1 && i > 0) { |
|||
while (i > 0 && (this._s1 = curSeg[--i]) >= v) { } |
|||
if (i === 0 && v < this._s1) { |
|||
this._s1 = 0; |
|||
} else { |
|||
i++; |
|||
} |
|||
this._s2 = curSeg[i]; |
|||
this._si = i; |
|||
} |
|||
t = ((i + (v - this._s1) / (this._s2 - this._s1)) * this._prec) || 0; |
|||
} |
|||
inv = 1 - t; |
|||
|
|||
i = this._props.length; |
|||
while (--i > -1) { |
|||
p = this._props[i]; |
|||
b = this._beziers[p][curIndex]; |
|||
val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a; |
|||
if (this._mod[p]) { |
|||
val = this._mod[p](val, target); |
|||
} |
|||
if (func[p]) { |
|||
target[p](val); |
|||
} else { |
|||
target[p] = val; |
|||
} |
|||
} |
|||
|
|||
if (this._autoRotate) { |
|||
var ar = this._autoRotate, |
|||
b2, x1, y1, x2, y2, add, conv; |
|||
i = ar.length; |
|||
while (--i > -1) { |
|||
p = ar[i][2]; |
|||
add = ar[i][3] || 0; |
|||
conv = (ar[i][4] === true) ? 1 : _RAD2DEG; |
|||
b = this._beziers[ar[i][0]]; |
|||
b2 = this._beziers[ar[i][1]]; |
|||
|
|||
if (b && b2) { //in case one of the properties got overwritten.
|
|||
b = b[curIndex]; |
|||
b2 = b2[curIndex]; |
|||
|
|||
x1 = b.a + (b.b - b.a) * t; |
|||
x2 = b.b + (b.c - b.b) * t; |
|||
x1 += (x2 - x1) * t; |
|||
x2 += ((b.c + (b.d - b.c) * t) - x2) * t; |
|||
|
|||
y1 = b2.a + (b2.b - b2.a) * t; |
|||
y2 = b2.b + (b2.c - b2.b) * t; |
|||
y1 += (y2 - y1) * t; |
|||
y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t; |
|||
|
|||
val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i]; |
|||
|
|||
if (this._mod[p]) { |
|||
val = this._mod[p](val, target); //for modProps
|
|||
} |
|||
|
|||
if (func[p]) { |
|||
target[p](val); |
|||
} else { |
|||
target[p] = val; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}), |
|||
p = BezierPlugin.prototype; |
|||
|
|||
|
|||
BezierPlugin.bezierThrough = bezierThrough; |
|||
BezierPlugin.cubicToQuadratic = cubicToQuadratic; |
|||
BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
|
|||
BezierPlugin.quadraticToCubic = function(a, b, c) { |
|||
return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c); |
|||
}; |
|||
|
|||
BezierPlugin._cssRegister = function() { |
|||
var CSSPlugin = _globals.CSSPlugin; |
|||
if (!CSSPlugin) { |
|||
return; |
|||
} |
|||
var _internals = CSSPlugin._internals, |
|||
_parseToProxy = _internals._parseToProxy, |
|||
_setPluginRatio = _internals._setPluginRatio, |
|||
CSSPropTween = _internals.CSSPropTween; |
|||
_internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) { |
|||
if (e instanceof Array) { |
|||
e = {values:e}; |
|||
} |
|||
plugin = new BezierPlugin(); |
|||
var values = e.values, |
|||
l = values.length - 1, |
|||
pluginValues = [], |
|||
v = {}, |
|||
i, p, data; |
|||
if (l < 0) { |
|||
return pt; |
|||
} |
|||
for (i = 0; i <= l; i++) { |
|||
data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i)); |
|||
pluginValues[i] = data.end; |
|||
} |
|||
for (p in e) { |
|||
v[p] = e[p]; //duplicate the vars object because we need to alter some things which would cause problems if the user plans to reuse the same vars object for another tween.
|
|||
} |
|||
v.values = pluginValues; |
|||
pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2); |
|||
pt.data = data; |
|||
pt.plugin = plugin; |
|||
pt.setRatio = _setPluginRatio; |
|||
if (v.autoRotate === 0) { |
|||
v.autoRotate = true; |
|||
} |
|||
if (v.autoRotate && !(v.autoRotate instanceof Array)) { |
|||
i = (v.autoRotate === true) ? 0 : Number(v.autoRotate); |
|||
v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false; |
|||
} |
|||
if (v.autoRotate) { |
|||
if (!cssp._transform) { |
|||
cssp._enableTransforms(false); |
|||
} |
|||
data.autoRotate = cssp._target._gsTransform; |
|||
data.proxy.rotation = data.autoRotate.rotation || 0; |
|||
cssp._overwriteProps.push("rotation"); |
|||
} |
|||
plugin._onInitTween(data.proxy, v, cssp._tween); |
|||
return pt; |
|||
}}); |
|||
}; |
|||
|
|||
p._mod = function(lookup) { |
|||
var op = this._overwriteProps, |
|||
i = op.length, |
|||
val; |
|||
while (--i > -1) { |
|||
val = lookup[op[i]]; |
|||
if (val && typeof(val) === "function") { |
|||
this._mod[op[i]] = val; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
p._kill = function(lookup) { |
|||
var a = this._props, |
|||
p, i; |
|||
for (p in this._beziers) { |
|||
if (p in lookup) { |
|||
delete this._beziers[p]; |
|||
delete this._func[p]; |
|||
i = a.length; |
|||
while (--i > -1) { |
|||
if (a[i] === p) { |
|||
a.splice(i, 1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
a = this._autoRotate; |
|||
if (a) { |
|||
i = a.length; |
|||
while (--i > -1) { |
|||
if (lookup[a[i][2]]) { |
|||
a.splice(i, 1); |
|||
} |
|||
} |
|||
} |
|||
return this._super._kill.call(this, lookup); |
|||
}; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("BezierPlugin")); |
|||
@ -0,0 +1,119 @@ |
|||
/*! |
|||
* VERSION: 0.6.6 |
|||
* DATE: 2017-06-29 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine("plugins.CSSRulePlugin", ["plugins.TweenPlugin","TweenLite","plugins.CSSPlugin"], function(TweenPlugin, TweenLite, CSSPlugin) { |
|||
|
|||
/** @constructor **/ |
|||
var CSSRulePlugin = function() { |
|||
TweenPlugin.call(this, "cssRule"); |
|||
this._overwriteProps.length = 0; |
|||
}, |
|||
_doc = _gsScope.document, |
|||
_superSetRatio = CSSPlugin.prototype.setRatio, |
|||
p = CSSRulePlugin.prototype = new CSSPlugin(); |
|||
|
|||
p._propName = "cssRule"; |
|||
p.constructor = CSSRulePlugin; |
|||
CSSRulePlugin.version = "0.6.6"; |
|||
CSSRulePlugin.API = 2; |
|||
|
|||
/** |
|||
* Searches the style sheets in the document for a particular selector like ".myClass" or "a" or "a:hover" or ":after" and |
|||
* returns a reference to that style sheet (or an array of them in the case of a pseudo selector like ":after"). Then you |
|||
* can animate the individual properties of the style sheet. |
|||
* |
|||
* @param {!string} selector a string describing the selector, like ".myClass" or "a" or "a:hover" or ":after" |
|||
* @return a reference to the style sheet (or an array of them in the case of a pseudo selector). If none was found, null is returned (or an empty array for a pseudo selector) |
|||
*/ |
|||
CSSRulePlugin.getRule = function(selector) { |
|||
var ruleProp = _doc.all ? 'rules' : 'cssRules', |
|||
ss = _doc.styleSheets, |
|||
i = ss.length, |
|||
pseudo = (selector.charAt(0) === ":"), |
|||
j, curSS, cs, a; |
|||
selector = (pseudo ? "" : ",") + selector.split("::").join(":").toLowerCase() + ","; //note: old versions of IE report tag name selectors as upper case, so we just change everything to lowercase.
|
|||
if (pseudo) { |
|||
a = []; |
|||
} |
|||
while (--i > -1) { |
|||
//Firefox may throw insecure operation errors when css is loaded from other domains, so try/catch.
|
|||
try { |
|||
curSS = ss[i][ruleProp]; |
|||
if (!curSS) { |
|||
continue; |
|||
} |
|||
j = curSS.length; |
|||
} catch (e) { |
|||
console.log(e); |
|||
continue; |
|||
} |
|||
while (--j > -1) { |
|||
cs = curSS[j]; |
|||
if (cs.selectorText && ("," + cs.selectorText.split("::").join(":").toLowerCase() + ",").indexOf(selector) !== -1) { //note: IE adds an extra ":" to pseudo selectors, so .myClass:after becomes .myClass::after, so we need to strip the extra one out.
|
|||
if (pseudo) { |
|||
a.push(cs.style); |
|||
} else { |
|||
return cs.style; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return a; |
|||
}; |
|||
|
|||
|
|||
// @private gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc.
|
|||
p._onInitTween = function(target, value, tween) { |
|||
if (target.cssText === undefined) { |
|||
return false; |
|||
} |
|||
var div = target._gsProxy = target._gsProxy || _doc.createElement("div"); |
|||
this._ss = target; |
|||
this._proxy = div.style; |
|||
div.style.cssText = target.cssText; |
|||
CSSPlugin.prototype._onInitTween.call(this, div, value, tween); //we just offload all the work to the regular CSSPlugin and then copy the cssText back over to the rule in the setRatio() method. This allows us to have all of the updates to CSSPlugin automatically flow through to CSSRulePlugin instead of having to maintain both
|
|||
return true; |
|||
}; |
|||
|
|||
|
|||
|
|||
// @private gets called every time the tween updates, passing the new ratio (typically a value between 0 and 1, but not always (for example, if an Elastic.easeOut is used, the value can jump above 1 mid-tween). It will always start and 0 and end at 1.
|
|||
p.setRatio = function(v) { |
|||
_superSetRatio.call(this, v); |
|||
this._ss.cssText = this._proxy.cssText; |
|||
}; |
|||
|
|||
|
|||
TweenPlugin.activate([CSSRulePlugin]); |
|||
return CSSRulePlugin; |
|||
|
|||
}, true); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("CSSRulePlugin")); |
|||
@ -0,0 +1,245 @@ |
|||
/*! |
|||
* VERSION: beta 1.5.2 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _numExp = /(\d|\.)+/g, |
|||
_relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g, |
|||
_colorLookup = {aqua:[0,255,255], |
|||
lime:[0,255,0], |
|||
silver:[192,192,192], |
|||
black:[0,0,0], |
|||
maroon:[128,0,0], |
|||
teal:[0,128,128], |
|||
blue:[0,0,255], |
|||
navy:[0,0,128], |
|||
white:[255,255,255], |
|||
fuchsia:[255,0,255], |
|||
olive:[128,128,0], |
|||
yellow:[255,255,0], |
|||
orange:[255,165,0], |
|||
gray:[128,128,128], |
|||
purple:[128,0,128], |
|||
green:[0,128,0], |
|||
red:[255,0,0], |
|||
pink:[255,192,203], |
|||
cyan:[0,255,255], |
|||
transparent:[255,255,255,0]}, |
|||
_hue = function(h, m1, m2) { |
|||
h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h; |
|||
return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0; |
|||
}, |
|||
/** |
|||
* @private Parses a color (like #9F0, #FF9900, rgb(255,51,153) or hsl(108, 50%, 10%)) into an array with 3 elements for red, green, and blue or if toHSL parameter is true, it will populate the array with hue, saturation, and lightness values. If a relative value is found in an hsl() or hsla() string, it will preserve those relative prefixes and all the values in the array will be strings instead of numbers (in all other cases it will be populated with numbers). |
|||
* @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc. |
|||
* @param {(boolean)} toHSL If true, an hsl() or hsla() value will be returned instead of rgb() or rgba() |
|||
* @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order, or if the toHSL parameter was true, the array will contain hue, saturation and lightness (and optionally alpha) in that order. Always numbers unless there's a relative prefix found in an hsl() or hsla() string and toHSL is true. |
|||
*/ |
|||
_parseColor = function(v, toHSL) { |
|||
var a, r, g, b, h, s, l, max, min, d, wasHSL; |
|||
if (!v) { |
|||
a = _colorLookup.black; |
|||
} else if (typeof(v) === "number") { |
|||
a = [v >> 16, (v >> 8) & 255, v & 255]; |
|||
} else { |
|||
if (v.charAt(v.length - 1) === ",") { //sometimes a trailing comma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value.
|
|||
v = v.substr(0, v.length - 1); |
|||
} |
|||
if (_colorLookup[v]) { |
|||
a = _colorLookup[v]; |
|||
} else if (v.charAt(0) === "#") { |
|||
if (v.length === 4) { //for shorthand like #9F0
|
|||
r = v.charAt(1); |
|||
g = v.charAt(2); |
|||
b = v.charAt(3); |
|||
v = "#" + r + r + g + g + b + b; |
|||
} |
|||
v = parseInt(v.substr(1), 16); |
|||
a = [v >> 16, (v >> 8) & 255, v & 255]; |
|||
} else if (v.substr(0, 3) === "hsl") { |
|||
a = wasHSL = v.match(_numExp); |
|||
if (!toHSL) { |
|||
h = (Number(a[0]) % 360) / 360; |
|||
s = Number(a[1]) / 100; |
|||
l = Number(a[2]) / 100; |
|||
g = (l <= 0.5) ? l * (s + 1) : l + s - l * s; |
|||
r = l * 2 - g; |
|||
if (a.length > 3) { |
|||
a[3] = Number(v[3]); |
|||
} |
|||
a[0] = _hue(h + 1 / 3, r, g); |
|||
a[1] = _hue(h, r, g); |
|||
a[2] = _hue(h - 1 / 3, r, g); |
|||
} else if (v.indexOf("=") !== -1) { //if relative values are found, just return the raw strings with the relative prefixes in place.
|
|||
return v.match(_relNumExp); |
|||
} |
|||
} else { |
|||
a = v.match(_numExp) || _colorLookup.transparent; |
|||
} |
|||
a[0] = Number(a[0]); |
|||
a[1] = Number(a[1]); |
|||
a[2] = Number(a[2]); |
|||
if (a.length > 3) { |
|||
a[3] = Number(a[3]); |
|||
} |
|||
} |
|||
if (toHSL && !wasHSL) { |
|||
r = a[0] / 255; |
|||
g = a[1] / 255; |
|||
b = a[2] / 255; |
|||
max = Math.max(r, g, b); |
|||
min = Math.min(r, g, b); |
|||
l = (max + min) / 2; |
|||
if (max === min) { |
|||
h = s = 0; |
|||
} else { |
|||
d = max - min; |
|||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min); |
|||
h = (max === r) ? (g - b) / d + (g < b ? 6 : 0) : (max === g) ? (b - r) / d + 2 : (r - g) / d + 4; |
|||
h *= 60; |
|||
} |
|||
a[0] = (h + 0.5) | 0; |
|||
a[1] = (s * 100 + 0.5) | 0; |
|||
a[2] = (l * 100 + 0.5) | 0; |
|||
} |
|||
return a; |
|||
}, |
|||
_formatColors = function(s, toHSL) { |
|||
var colors = (s + "").match(_colorExp) || [], |
|||
charIndex = 0, |
|||
parsed = "", |
|||
i, color, temp; |
|||
if (!colors.length) { |
|||
return s; |
|||
} |
|||
for (i = 0; i < colors.length; i++) { |
|||
color = colors[i]; |
|||
temp = s.substr(charIndex, s.indexOf(color, charIndex)-charIndex); |
|||
charIndex += temp.length + color.length; |
|||
color = _parseColor(color, toHSL); |
|||
if (color.length === 3) { |
|||
color.push(1); |
|||
} |
|||
parsed += temp + (toHSL ? "hsla(" + color[0] + "," + color[1] + "%," + color[2] + "%," + color[3] : "rgba(" + color.join(",")) + ")"; |
|||
} |
|||
return parsed + s.substr(charIndex); |
|||
}, p, _colorStringFilter, |
|||
TweenLite = (_gsScope.GreenSockGlobals || _gsScope).TweenLite, |
|||
_colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3}){1,2}\\b", //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc.
|
|||
|
|||
ColorPropsPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "colorProps", |
|||
version: "1.5.2", |
|||
priority: -1, |
|||
API: 2, |
|||
global: true, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween, index) { |
|||
var p, proxy, pt, val; |
|||
this._target = target; |
|||
this._proxy = proxy = ((value.format + "").toUpperCase() === "NUMBER") ? {} : 0; |
|||
for (p in value) { |
|||
if (p !== "format") { |
|||
if (proxy) { |
|||
this._firstNumPT = pt = {_next:this._firstNumPT, t:target, p:p, f:(typeof(target[p]) === "function")}; |
|||
proxy[p] = "rgb(" + _parseColor(!pt.f ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]()).join(",") + ")"; |
|||
val = value[p]; |
|||
if (typeof(val) === "function") { |
|||
val = val(index, target); |
|||
} |
|||
this._addTween(proxy, p, "get", ((typeof(val) === "number") ? "rgb(" + _parseColor(val, false).join(",") + ")" : val), p, null, null, _colorStringFilter); |
|||
} else { |
|||
this._addTween(target, p, "get", value[p], p, null, null, _colorStringFilter, index); |
|||
} |
|||
|
|||
} |
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(v) { |
|||
var pt = this._firstNumPT, |
|||
val; |
|||
this._super.setRatio.call(this, v); |
|||
while (pt) { |
|||
val = _parseColor(this._proxy[pt.p], false); |
|||
val = val[0] << 16 | val[1] << 8 | val[2]; |
|||
if (pt.f) { |
|||
this._target[pt.p](val); |
|||
} else { |
|||
this._target[pt.p] = val; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
for (p in _colorLookup) { |
|||
_colorExp += "|" + p + "\\b"; |
|||
} |
|||
_colorExp = new RegExp(_colorExp+")", "gi"); |
|||
ColorPropsPlugin.colorStringFilter = _colorStringFilter = function(a) { |
|||
var combined = a[0] + " " + a[1], |
|||
toHSL; |
|||
_colorExp.lastIndex = 0; |
|||
if (_colorExp.test(combined)) { |
|||
toHSL = (combined.indexOf("hsl(") !== -1 || combined.indexOf("hsla(") !== -1); |
|||
a[0] = _formatColors(a[0], toHSL); |
|||
a[1] = _formatColors(a[1], toHSL); |
|||
} |
|||
}; |
|||
|
|||
if (!TweenLite.defaultStringFilter) { |
|||
TweenLite.defaultStringFilter = ColorPropsPlugin.colorStringFilter; |
|||
} |
|||
|
|||
ColorPropsPlugin.parseColor = _parseColor; |
|||
p = ColorPropsPlugin.prototype; |
|||
p._firstNumPT = null; |
|||
p._kill = function(lookup) { |
|||
var pt = this._firstNumPT, |
|||
prev; |
|||
while (pt) { |
|||
if (pt.p in lookup) { |
|||
if (pt === p._firstNumPT) { |
|||
this._firstNumPT = pt._next; |
|||
} |
|||
if (prev) { |
|||
prev._next = pt._next; |
|||
} |
|||
} else { |
|||
prev = pt; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
return this._super._kill(lookup); |
|||
}; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("ColorPropsPlugin")); |
|||
@ -0,0 +1,99 @@ |
|||
/*! |
|||
* VERSION: 0.3.1 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine.plugin({ |
|||
propName: "directionalRotation", |
|||
version: "0.3.1", |
|||
API: 2, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween, index) { |
|||
if (typeof(value) !== "object") { |
|||
value = {rotation:value}; |
|||
} |
|||
this.finals = {}; |
|||
var cap = (value.useRadians === true) ? Math.PI * 2 : 360, |
|||
min = 0.000001, |
|||
p, v, start, end, dif, split; |
|||
for (p in value) { |
|||
if (p !== "useRadians") { |
|||
end = value[p]; |
|||
if (typeof(end) === "function") { |
|||
end = end(index, target); |
|||
} |
|||
split = (end + "").split("_"); |
|||
v = split[0]; |
|||
start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() ); |
|||
end = this.finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0; |
|||
dif = end - start; |
|||
if (split.length) { |
|||
v = split.join("_"); |
|||
if (v.indexOf("short") !== -1) { |
|||
dif = dif % cap; |
|||
if (dif !== dif % (cap / 2)) { |
|||
dif = (dif < 0) ? dif + cap : dif - cap; |
|||
} |
|||
} |
|||
if (v.indexOf("_cw") !== -1 && dif < 0) { |
|||
dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; |
|||
} else if (v.indexOf("ccw") !== -1 && dif > 0) { |
|||
dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap; |
|||
} |
|||
} |
|||
if (dif > min || dif < -min) { |
|||
this._addTween(target, p, start, start + dif, p); |
|||
this._overwriteProps.push(p); |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(ratio) { |
|||
var pt; |
|||
if (ratio !== 1) { |
|||
this._super.setRatio.call(this, ratio); |
|||
} else { |
|||
pt = this._firstPT; |
|||
while (pt) { |
|||
if (pt.f) { |
|||
pt.t[pt.p](this.finals[pt.p]); |
|||
} else { |
|||
pt.t[pt.p] = this.finals[pt.p]; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
} |
|||
} |
|||
|
|||
})._autoCSS = true; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("DirectionalRotationPlugin")); |
|||
@ -0,0 +1,324 @@ |
|||
/*! |
|||
* VERSION: 0.2.2 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _numExp = /(\d|\.)+/g, |
|||
_ColorFilter, _ColorMatrixFilter, |
|||
_colorProps = ["redMultiplier","greenMultiplier","blueMultiplier","alphaMultiplier","redOffset","greenOffset","blueOffset","alphaOffset"], |
|||
_colorLookup = {aqua:[0,255,255], |
|||
lime:[0,255,0], |
|||
silver:[192,192,192], |
|||
black:[0,0,0], |
|||
maroon:[128,0,0], |
|||
teal:[0,128,128], |
|||
blue:[0,0,255], |
|||
navy:[0,0,128], |
|||
white:[255,255,255], |
|||
fuchsia:[255,0,255], |
|||
olive:[128,128,0], |
|||
yellow:[255,255,0], |
|||
orange:[255,165,0], |
|||
gray:[128,128,128], |
|||
purple:[128,0,128], |
|||
green:[0,128,0], |
|||
red:[255,0,0], |
|||
pink:[255,192,203], |
|||
cyan:[0,255,255], |
|||
transparent:[255,255,255,0]}, |
|||
_parseColor = function(color) { |
|||
if (color === "" || color == null || color === "none") { |
|||
return _colorLookup.transparent; |
|||
} else if (_colorLookup[color]) { |
|||
return _colorLookup[color]; |
|||
} else if (typeof(color) === "number") { |
|||
return [color >> 16, (color >> 8) & 255, color & 255]; |
|||
} else if (color.charAt(0) === "#") { |
|||
if (color.length === 4) { //for shorthand like #9F0
|
|||
color = "#" + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2) + color.charAt(3) + color.charAt(3); |
|||
} |
|||
color = parseInt(color.substr(1), 16); |
|||
return [color >> 16, (color >> 8) & 255, color & 255]; |
|||
} |
|||
return color.match(_numExp) || _colorLookup.transparent; |
|||
}, |
|||
_parseColorFilter = function(t, v, pg) { |
|||
if (!_ColorFilter) { |
|||
_ColorFilter = (_gsScope.ColorFilter || _gsScope.createjs.ColorFilter); |
|||
if (!_ColorFilter) { |
|||
throw("EaselPlugin error: The EaselJS ColorFilter JavaScript file wasn't loaded."); |
|||
} |
|||
} |
|||
var filters = t.filters || [], |
|||
i = filters.length, |
|||
c, s, e, a, p; |
|||
while (--i > -1) { |
|||
if (filters[i] instanceof _ColorFilter) { |
|||
s = filters[i]; |
|||
break; |
|||
} |
|||
} |
|||
if (!s) { |
|||
s = new _ColorFilter(); |
|||
filters.push(s); |
|||
t.filters = filters; |
|||
} |
|||
e = s.clone(); |
|||
if (v.tint != null) { |
|||
c = _parseColor(v.tint); |
|||
a = (v.tintAmount != null) ? Number(v.tintAmount) : 1; |
|||
e.redOffset = Number(c[0]) * a; |
|||
e.greenOffset = Number(c[1]) * a; |
|||
e.blueOffset = Number(c[2]) * a; |
|||
e.redMultiplier = e.greenMultiplier = e.blueMultiplier = 1 - a; |
|||
} else { |
|||
for (p in v) { |
|||
if (p !== "exposure") if (p !== "brightness") { |
|||
e[p] = Number(v[p]); |
|||
} |
|||
} |
|||
} |
|||
if (v.exposure != null) { |
|||
e.redOffset = e.greenOffset = e.blueOffset = 255 * (Number(v.exposure) - 1); |
|||
e.redMultiplier = e.greenMultiplier = e.blueMultiplier = 1; |
|||
} else if (v.brightness != null) { |
|||
a = Number(v.brightness) - 1; |
|||
e.redOffset = e.greenOffset = e.blueOffset = (a > 0) ? a * 255 : 0; |
|||
e.redMultiplier = e.greenMultiplier = e.blueMultiplier = 1 - Math.abs(a); |
|||
} |
|||
i = 8; |
|||
while (--i > -1) { |
|||
p = _colorProps[i]; |
|||
if (s[p] !== e[p]) { |
|||
pg._addTween(s, p, s[p], e[p], "easel_colorFilter"); |
|||
} |
|||
} |
|||
pg._overwriteProps.push("easel_colorFilter"); |
|||
if (!t.cacheID) { |
|||
throw("EaselPlugin warning: for filters to display in EaselJS, you must call the object's cache() method first. " + t); |
|||
} |
|||
}, |
|||
|
|||
_idMatrix = [1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0], |
|||
_lumR = 0.212671, |
|||
_lumG = 0.715160, |
|||
_lumB = 0.072169, |
|||
|
|||
_applyMatrix = function(m, m2) { |
|||
if (!(m instanceof Array) || !(m2 instanceof Array)) { |
|||
return m2; |
|||
} |
|||
var temp = [], |
|||
i = 0, |
|||
z = 0, |
|||
y, x; |
|||
for (y = 0; y < 4; y++) { |
|||
for (x = 0; x < 5; x++) { |
|||
z = (x === 4) ? m[i + 4] : 0; |
|||
temp[i + x] = m[i] * m2[x] + m[i+1] * m2[x + 5] + m[i+2] * m2[x + 10] + m[i+3] * m2[x + 15] + z; |
|||
} |
|||
i += 5; |
|||
} |
|||
return temp; |
|||
}, |
|||
|
|||
_setSaturation = function(m, n) { |
|||
if (isNaN(n)) { |
|||
return m; |
|||
} |
|||
var inv = 1 - n, |
|||
r = inv * _lumR, |
|||
g = inv * _lumG, |
|||
b = inv * _lumB; |
|||
return _applyMatrix([r + n, g, b, 0, 0, r, g + n, b, 0, 0, r, g, b + n, 0, 0, 0, 0, 0, 1, 0], m); |
|||
}, |
|||
|
|||
_colorize = function(m, color, amount) { |
|||
if (isNaN(amount)) { |
|||
amount = 1; |
|||
} |
|||
var c = _parseColor(color), |
|||
r = c[0] / 255, |
|||
g = c[1] / 255, |
|||
b = c[2] / 255, |
|||
inv = 1 - amount; |
|||
return _applyMatrix([inv + amount * r * _lumR, amount * r * _lumG, amount * r * _lumB, 0, 0, amount * g * _lumR, inv + amount * g * _lumG, amount * g * _lumB, 0, 0, amount * b * _lumR, amount * b * _lumG, inv + amount * b * _lumB, 0, 0, 0, 0, 0, 1, 0], m); |
|||
}, |
|||
|
|||
_setHue = function(m, n) { |
|||
if (isNaN(n)) { |
|||
return m; |
|||
} |
|||
n *= Math.PI / 180; |
|||
var c = Math.cos(n), |
|||
s = Math.sin(n); |
|||
return _applyMatrix([(_lumR + (c * (1 - _lumR))) + (s * (-_lumR)), (_lumG + (c * (-_lumG))) + (s * (-_lumG)), (_lumB + (c * (-_lumB))) + (s * (1 - _lumB)), 0, 0, (_lumR + (c * (-_lumR))) + (s * 0.143), (_lumG + (c * (1 - _lumG))) + (s * 0.14), (_lumB + (c * (-_lumB))) + (s * -0.283), 0, 0, (_lumR + (c * (-_lumR))) + (s * (-(1 - _lumR))), (_lumG + (c * (-_lumG))) + (s * _lumG), (_lumB + (c * (1 - _lumB))) + (s * _lumB), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1], m); |
|||
}, |
|||
|
|||
_setContrast = function(m, n) { |
|||
if (isNaN(n)) { |
|||
return m; |
|||
} |
|||
n += 0.01; |
|||
return _applyMatrix([n,0,0,0,128 * (1 - n), 0,n,0,0,128 * (1 - n), 0,0,n,0,128 * (1 - n), 0,0,0,1,0], m); |
|||
}, |
|||
|
|||
_parseColorMatrixFilter = function(t, v, pg) { |
|||
if (!_ColorMatrixFilter) { |
|||
_ColorMatrixFilter = (_gsScope.ColorMatrixFilter || _gsScope.createjs.ColorMatrixFilter); |
|||
if (!_ColorMatrixFilter) { |
|||
throw("EaselPlugin error: The EaselJS ColorMatrixFilter JavaScript file wasn't loaded."); |
|||
} |
|||
} |
|||
var filters = t.filters || [], |
|||
i = filters.length, |
|||
matrix, startMatrix, s; |
|||
while (--i > -1) { |
|||
if (filters[i] instanceof _ColorMatrixFilter) { |
|||
s = filters[i]; |
|||
break; |
|||
} |
|||
} |
|||
if (!s) { |
|||
s = new _ColorMatrixFilter(_idMatrix.slice()); |
|||
filters.push(s); |
|||
t.filters = filters; |
|||
} |
|||
startMatrix = s.matrix; |
|||
matrix = _idMatrix.slice(); |
|||
if (v.colorize != null) { |
|||
matrix = _colorize(matrix, v.colorize, Number(v.colorizeAmount)); |
|||
} |
|||
if (v.contrast != null) { |
|||
matrix = _setContrast(matrix, Number(v.contrast)); |
|||
} |
|||
if (v.hue != null) { |
|||
matrix = _setHue(matrix, Number(v.hue)); |
|||
} |
|||
if (v.saturation != null) { |
|||
matrix = _setSaturation(matrix, Number(v.saturation)); |
|||
} |
|||
|
|||
i = matrix.length; |
|||
while (--i > -1) { |
|||
if (matrix[i] !== startMatrix[i]) { |
|||
pg._addTween(startMatrix, i, startMatrix[i], matrix[i], "easel_colorMatrixFilter"); |
|||
} |
|||
} |
|||
|
|||
pg._overwriteProps.push("easel_colorMatrixFilter"); |
|||
if (!t.cacheID) { |
|||
throw("EaselPlugin warning: for filters to display in EaselJS, you must call the object's cache() method first. " + t); |
|||
} |
|||
|
|||
pg._matrix = startMatrix; |
|||
}; |
|||
|
|||
|
|||
_gsScope._gsDefine.plugin({ |
|||
propName: "easel", |
|||
priority: -1, |
|||
version: "0.2.2", |
|||
API: 2, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween, index) { |
|||
this._target = target; |
|||
var p, pt, tint, colorMatrix, end, labels, i; |
|||
for (p in value) { |
|||
|
|||
end = value[p]; |
|||
if (typeof(end) === "function") { |
|||
end = end(index, target); |
|||
} |
|||
if (p === "colorFilter" || p === "tint" || p === "tintAmount" || p === "exposure" || p === "brightness") { |
|||
if (!tint) { |
|||
_parseColorFilter(target, value.colorFilter || value, this); |
|||
tint = true; |
|||
} |
|||
|
|||
} else if (p === "saturation" || p === "contrast" || p === "hue" || p === "colorize" || p === "colorizeAmount") { |
|||
if (!colorMatrix) { |
|||
_parseColorMatrixFilter(target, value.colorMatrixFilter || value, this); |
|||
colorMatrix = true; |
|||
} |
|||
|
|||
} else if (p === "frame") { |
|||
this._firstPT = pt = {_next:this._firstPT, t:target, p:"gotoAndStop", s:target.currentFrame, f:true, n:"frame", pr:0, type:0, m:Math.round}; |
|||
if (typeof(end) === "string" && end.charAt(1) !== "=" && (labels = target.labels)) { |
|||
for (i = 0; i < labels.length; i++) { |
|||
if (labels[i].label === end) { |
|||
end = labels[i].position; |
|||
} |
|||
} |
|||
} |
|||
pt.c = (typeof(end) === "number") ? end - pt.s : parseFloat((end+"").split("=").join("")); |
|||
if (pt._next) { |
|||
pt._next._prev = pt; |
|||
} |
|||
|
|||
} else if (target[p] != null) { |
|||
this._firstPT = pt = {_next:this._firstPT, t:target, p:p, f:(typeof(target[p]) === "function"), n:p, pr:0, type:0}; |
|||
pt.s = (!pt.f) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ](); |
|||
pt.c = (typeof(end) === "number") ? end - pt.s : (typeof(end) === "string") ? parseFloat(end.split("=").join("")) : 0; |
|||
|
|||
if (pt._next) { |
|||
pt._next._prev = pt; |
|||
} |
|||
} |
|||
|
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(v) { |
|||
var pt = this._firstPT, |
|||
min = 0.000001, |
|||
val; |
|||
while (pt) { |
|||
val = pt.c * v + pt.s; |
|||
if (pt.m) { |
|||
val = pt.m(val, pt.t); |
|||
} else if (val < min && val > -min) { |
|||
val = 0; |
|||
} |
|||
if (pt.f) { |
|||
pt.t[pt.p](val); |
|||
} else { |
|||
pt.t[pt.p] = val; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
if (this._target.cacheID) { |
|||
this._target.updateCache(); |
|||
} |
|||
} |
|||
|
|||
}); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("EaselPlugin")); |
|||
@ -0,0 +1,71 @@ |
|||
/*! |
|||
* VERSION: 0.1.3 |
|||
* DATE: 2017-01-17 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine.plugin({ |
|||
propName: "endArray", |
|||
API: 2, |
|||
version: "0.1.3", |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween) { |
|||
var i = value.length, |
|||
a = this.a = [], |
|||
start, end; |
|||
this.target = target; |
|||
this._mod = 0; |
|||
if (!i) { |
|||
return false; |
|||
} |
|||
while (--i > -1) { |
|||
start = target[i]; |
|||
end = value[i]; |
|||
if (start !== end) { |
|||
a.push({i:i, s:start, c:end - start}); |
|||
} |
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
mod: function(lookup) { |
|||
if (typeof(lookup.endArray) === "function") { |
|||
this._mod = lookup.endArray; |
|||
} |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(ratio) { |
|||
var target = this.target, |
|||
a = this.a, |
|||
i = a.length, |
|||
mod = this._mod, |
|||
e, val; |
|||
if (mod) { |
|||
while (--i > -1) { |
|||
e = a[i]; |
|||
target[e.i] = mod(e.s + e.c * ratio, target); |
|||
} |
|||
} else { |
|||
while (--i > -1) { |
|||
e = a[i]; |
|||
val = e.s + e.c * ratio; |
|||
target[e.i] = (val < 0.000001 && val > -0.000001) ? 0 : val; |
|||
} |
|||
} |
|||
} |
|||
|
|||
}); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
@ -0,0 +1,172 @@ |
|||
/*! |
|||
* VERSION: 0.0.3 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _cssRatioSetter = function(pt, cssp, mod) { //Takes an individual CSSPropTween and converts it into a type:2 that has a setRatio that does everything the regular CSSPlugin.setRatio() method does but applying the mod() too. We do this to keep the main CSSPlugin.setRatio() as fast as possible (the vast majority of times, no mod() will be necessary)
|
|||
var type = pt.type, |
|||
oldSetRatio = pt.setRatio, |
|||
tween = cssp._tween, |
|||
target = cssp._target; |
|||
pt.type = 2; |
|||
pt.m = mod; |
|||
pt.setRatio = function(v) { |
|||
var min = 0.000001, |
|||
val, str, i; |
|||
if (v === 1 && (tween._time === tween._duration || tween._time === 0)) { |
|||
|
|||
if (type !== 2) { |
|||
if (pt.r && type !== -1) { |
|||
val = Math.round(pt.s + pt.c); |
|||
if (!type) { |
|||
pt.t[pt.p] = mod(val + pt.xs0, target); |
|||
} else if (type === 1) { |
|||
str = pt.xs0 + val + pt.xs1; |
|||
for (i = 1; i < pt.l; i++) { |
|||
str += pt["xn"+i] + pt["xs"+(i+1)]; |
|||
} |
|||
pt.t[pt.p] = mod(str, target); |
|||
} |
|||
} else { |
|||
pt.t[pt.p] = mod(pt.e, target); |
|||
} |
|||
} else { |
|||
oldSetRatio.call(pt, v); |
|||
} |
|||
|
|||
} else if (v || !(tween._time === tween._duration || tween._time === 0) || tween._rawPrevTime === -0.000001) { |
|||
val = pt.c * v + pt.s; |
|||
if (pt.r) { |
|||
val = Math.round(val); |
|||
} else if (val < min) if (val > -min) { |
|||
val = 0; |
|||
} |
|||
if (!type) { |
|||
pt.t[pt.p] = mod(val + pt.xs0, target); |
|||
} else if (type === 1) { |
|||
str = pt.xs0 + val + pt.xs1; |
|||
for (i = 1; i < pt.l; i++) { |
|||
str += pt["xn"+i] + pt["xs"+(i+1)]; |
|||
} |
|||
pt.t[pt.p] = mod(str, target); |
|||
|
|||
} else if (type === -1) { //non-tweening value
|
|||
pt.t[pt.p] = mod(pt.xs0, target); |
|||
|
|||
} else if (oldSetRatio) { |
|||
oldSetRatio.call(pt, v); |
|||
} |
|||
|
|||
} else { |
|||
if (type !== 2) { |
|||
pt.t[pt.p] = mod(pt.b, target); |
|||
} else { |
|||
oldSetRatio.call(pt, v); |
|||
} |
|||
} |
|||
}; |
|||
}, |
|||
_modCSS = function(lookup, cssp) { |
|||
var pt = cssp._firstPT, |
|||
hasBezier = (lookup.rotation && cssp._overwriteProps.join("").indexOf("bezier") !== -1); //when a Bezier tween is applying autoRotation, it's a very special case we need to handle differently.
|
|||
while (pt) { |
|||
if (typeof(lookup[pt.p]) === "function") { |
|||
_cssRatioSetter(pt, cssp, lookup[pt.p]); |
|||
} else if (hasBezier && pt.n === "bezier" && pt.plugin._overwriteProps.join("").indexOf("rotation") !== -1) { |
|||
pt.data.mod = lookup.rotation; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
}, |
|||
|
|||
ModifiersPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "modifiers", |
|||
version: "0.0.3", |
|||
API: 2, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween) { |
|||
this._tween = tween; |
|||
this._vars = value; |
|||
return true; |
|||
}, |
|||
|
|||
initAll: function() { |
|||
var tween = this._tween, |
|||
lookup = this._vars, |
|||
mpt = this, |
|||
pt = tween._firstPT, |
|||
val, next; |
|||
while (pt) { |
|||
next = pt._next; //record here, because it may get removed
|
|||
val = lookup[pt.n]; |
|||
if (pt.pg) { |
|||
if (pt.t._propName === "css") { //handle CSSPlugin uniquely (for performance, due to the fact that the values almost always are a concatenation of numbers and strings, like suffixes, and we don't want to slow down the regular CSSPlugin setRatio() performance with conditional checks for if the value needs to be modded, so we pull any modding prop out and change it to a type:2 one that simply calls a setRatio() method where we encapsulate the modding and update all together. That way, it says in the main CSSProp linked list and just has some custom logic applied to it inside its setRatio())
|
|||
_modCSS(lookup, pt.t); |
|||
} else if (pt.t !== mpt) { //don't run modProps on modProps :)
|
|||
val = lookup[pt.t._propName]; |
|||
pt.t._mod((typeof(val) === "object") ? val : lookup); |
|||
} |
|||
} else if (typeof(val) === "function") { |
|||
if (pt.f === 2 && pt.t) { //a blob (text containing multiple numeric values)
|
|||
pt.t._applyPT.m = val; |
|||
} else { |
|||
this._add(pt.t, pt.p, pt.s, pt.c, val); |
|||
//remove from linked list
|
|||
if (next) { |
|||
next._prev = pt._prev; |
|||
} |
|||
if (pt._prev) { |
|||
pt._prev._next = next; |
|||
} else if (tween._firstPT === pt) { |
|||
tween._firstPT = next; |
|||
} |
|||
pt._next = pt._prev = null; |
|||
tween._propLookup[pt.n] = mpt; |
|||
} |
|||
} |
|||
pt = next; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
}), |
|||
p = ModifiersPlugin.prototype; |
|||
|
|||
p._add = function(target, p, s, c, mod) { |
|||
this._addTween(target, p, s, s + c, p, mod); |
|||
this._overwriteProps.push(p); |
|||
}; |
|||
|
|||
p = _gsScope._gsDefine.globals.TweenLite.version.split("."); |
|||
if (Number(p[0]) <= 1 && Number(p[1]) < 19 && _gsScope.console) { |
|||
console.log("ModifiersPlugin requires GSAP 1.19.0 or later."); |
|||
} |
|||
|
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("ModifiersPlugin")); |
|||
@ -0,0 +1,468 @@ |
|||
/*! |
|||
* VERSION: 0.1.2 |
|||
* DATE: 2017-06-29 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* PixiPlugin is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof module !== "undefined" && module.exports && typeof global !== "undefined") ? global : this || window; |
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push(function () { |
|||
"use strict"; |
|||
|
|||
var _numExp = /(\d|\.)+/g, |
|||
_relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g, |
|||
_colorLookup = {aqua:[0,255,255], |
|||
lime:[0,255,0], |
|||
silver:[192,192,192], |
|||
black:[0,0,0], |
|||
maroon:[128,0,0], |
|||
teal:[0,128,128], |
|||
blue:[0,0,255], |
|||
navy:[0,0,128], |
|||
white:[255,255,255], |
|||
fuchsia:[255,0,255], |
|||
olive:[128,128,0], |
|||
yellow:[255,255,0], |
|||
orange:[255,165,0], |
|||
gray:[128,128,128], |
|||
purple:[128,0,128], |
|||
green:[0,128,0], |
|||
red:[255,0,0], |
|||
pink:[255,192,203], |
|||
cyan:[0,255,255], |
|||
transparent:[255,255,255,0]}, |
|||
_hue = function(h, m1, m2) { |
|||
h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h; |
|||
return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0; |
|||
}, |
|||
/** |
|||
* @private Parses a color (like #9F0, #FF9900, rgb(255,51,153) or hsl(108, 50%, 10%)) into an array with 3 elements for red, green, and blue or if "format" parameter is "hsl", it will populate the array with hue, saturation, and lightness values. Or if "format" is "number", it'll return a number like 0xFF0000 instead of an array. If a relative value is found in an hsl() or hsla() string, it will preserve those relative prefixes and all the values in the array will be strings instead of numbers (in all other cases it will be populated with numbers). |
|||
* @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc. |
|||
* @param {(string)} format If "hsl", an hsl() or hsla() value will be returned instead of rgb() or rgba(). Or if "number", then a numeric value will be returned, like 0xFF0000. Default is rgb. |
|||
* @return {(array|number)} An array containing red, green, and blue (and optionally alpha) in that order, or if the format parameter was "hsl", the array will contain hue, saturation and lightness (and optionally alpha) in that order. Or if "format" is defined as "number", it'll return a number like 0xFF0000. Always numbers unless there's a relative prefix found in an hsl() or hsla() string and "format" is "hsl". |
|||
*/ |
|||
_parseColor = function(v, format) { |
|||
var toHSL = (format === "hsl"), |
|||
a, r, g, b, h, s, l, max, min, d, wasHSL; |
|||
if (!v) { |
|||
a = _colorLookup.black; |
|||
} else if (typeof(v) === "number") { |
|||
a = [v >> 16, (v >> 8) & 255, v & 255]; |
|||
} else { |
|||
if (v.charAt(v.length - 1) === ",") { //sometimes a trailing comma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value.
|
|||
v = v.substr(0, v.length - 1); |
|||
} |
|||
if (_colorLookup[v]) { |
|||
a = _colorLookup[v]; |
|||
} else if (v.charAt(0) === "#") { |
|||
if (v.length === 4) { //for shorthand like #9F0
|
|||
r = v.charAt(1); |
|||
g = v.charAt(2); |
|||
b = v.charAt(3); |
|||
v = "#" + r + r + g + g + b + b; |
|||
} |
|||
v = parseInt(v.substr(1), 16); |
|||
a = [v >> 16, (v >> 8) & 255, v & 255]; |
|||
} else if (v.substr(0, 3) === "hsl") { |
|||
a = wasHSL = v.match(_numExp); |
|||
if (!toHSL) { |
|||
h = (Number(a[0]) % 360) / 360; |
|||
s = Number(a[1]) / 100; |
|||
l = Number(a[2]) / 100; |
|||
g = (l <= 0.5) ? l * (s + 1) : l + s - l * s; |
|||
r = l * 2 - g; |
|||
if (a.length > 3) { |
|||
a[3] = Number(v[3]); |
|||
} |
|||
a[0] = _hue(h + 1 / 3, r, g); |
|||
a[1] = _hue(h, r, g); |
|||
a[2] = _hue(h - 1 / 3, r, g); |
|||
} else if (v.indexOf("=") !== -1) { //if relative values are found, just return the raw strings with the relative prefixes in place.
|
|||
return v.match(_relNumExp); |
|||
} |
|||
} else { |
|||
a = v.match(_numExp) || _colorLookup.transparent; |
|||
} |
|||
a[0] = Number(a[0]); |
|||
a[1] = Number(a[1]); |
|||
a[2] = Number(a[2]); |
|||
if (a.length > 3) { |
|||
a[3] = Number(a[3]); |
|||
} |
|||
} |
|||
if (toHSL && !wasHSL) { |
|||
r = a[0] / 255; |
|||
g = a[1] / 255; |
|||
b = a[2] / 255; |
|||
max = Math.max(r, g, b); |
|||
min = Math.min(r, g, b); |
|||
l = (max + min) / 2; |
|||
if (max === min) { |
|||
h = s = 0; |
|||
} else { |
|||
d = max - min; |
|||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min); |
|||
h = (max === r) ? (g - b) / d + (g < b ? 6 : 0) : (max === g) ? (b - r) / d + 2 : (r - g) / d + 4; |
|||
h *= 60; |
|||
} |
|||
a[0] = (h + 0.5) | 0; |
|||
a[1] = (s * 100 + 0.5) | 0; |
|||
a[2] = (l * 100 + 0.5) | 0; |
|||
} |
|||
return (format === "number") ? (a[0] << 16 | a[1] << 8 | a[2]) : a; |
|||
}, |
|||
_formatColors = function(s, toHSL) { |
|||
var colors = (s + "").match(_colorExp) || [], |
|||
charIndex = 0, |
|||
parsed = "", |
|||
i, color, temp; |
|||
if (!colors.length) { |
|||
return s; |
|||
} |
|||
for (i = 0; i < colors.length; i++) { |
|||
color = colors[i]; |
|||
temp = s.substr(charIndex, s.indexOf(color, charIndex)-charIndex); |
|||
charIndex += temp.length + color.length; |
|||
color = _parseColor(color, (toHSL ? "hsl" : "rgb")); |
|||
if (color.length === 3) { |
|||
color.push(1); |
|||
} |
|||
parsed += temp + (toHSL ? "hsla(" + color[0] + "," + color[1] + "%," + color[2] + "%," + color[3] : "rgba(" + color.join(",")) + ")"; |
|||
} |
|||
return parsed + s.substr(charIndex); |
|||
}, _colorStringFilter, |
|||
TweenLite = (_gsScope.GreenSockGlobals || _gsScope).TweenLite, |
|||
_colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3}){1,2}\\b", //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc.
|
|||
|
|||
_idMatrix = [1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0], |
|||
_lumR = 0.212671, |
|||
_lumG = 0.715160, |
|||
_lumB = 0.072169, |
|||
|
|||
_applyMatrix = function(m, m2) { |
|||
var temp = [], |
|||
i = 0, |
|||
z = 0, |
|||
y, x; |
|||
for (y = 0; y < 4; y++) { |
|||
for (x = 0; x < 5; x++) { |
|||
z = (x === 4) ? m[i + 4] : 0; |
|||
temp[i + x] = m[i] * m2[x] + m[i+1] * m2[x + 5] + m[i+2] * m2[x + 10] + m[i+3] * m2[x + 15] + z; |
|||
} |
|||
i += 5; |
|||
} |
|||
return temp; |
|||
}, |
|||
|
|||
_setSaturation = function(m, n) { |
|||
var inv = 1 - n, |
|||
r = inv * _lumR, |
|||
g = inv * _lumG, |
|||
b = inv * _lumB; |
|||
return _applyMatrix([r + n, g, b, 0, 0, r, g + n, b, 0, 0, r, g, b + n, 0, 0, 0, 0, 0, 1, 0], m); |
|||
}, |
|||
|
|||
_colorize = function(m, color, amount) { |
|||
var c = _parseColor(color), |
|||
r = c[0] / 255, |
|||
g = c[1] / 255, |
|||
b = c[2] / 255, |
|||
inv = 1 - amount; |
|||
return _applyMatrix([inv + amount * r * _lumR, amount * r * _lumG, amount * r * _lumB, 0, 0, amount * g * _lumR, inv + amount * g * _lumG, amount * g * _lumB, 0, 0, amount * b * _lumR, amount * b * _lumG, inv + amount * b * _lumB, 0, 0, 0, 0, 0, 1, 0], m); |
|||
}, |
|||
|
|||
_setHue = function(m, n) { |
|||
n *= Math.PI / 180; |
|||
var c = Math.cos(n), |
|||
s = Math.sin(n); |
|||
return _applyMatrix([(_lumR + (c * (1 - _lumR))) + (s * (-_lumR)), (_lumG + (c * (-_lumG))) + (s * (-_lumG)), (_lumB + (c * (-_lumB))) + (s * (1 - _lumB)), 0, 0, (_lumR + (c * (-_lumR))) + (s * 0.143), (_lumG + (c * (1 - _lumG))) + (s * 0.14), (_lumB + (c * (-_lumB))) + (s * -0.283), 0, 0, (_lumR + (c * (-_lumR))) + (s * (-(1 - _lumR))), (_lumG + (c * (-_lumG))) + (s * _lumG), (_lumB + (c * (1 - _lumB))) + (s * _lumB), 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1], m); |
|||
}, |
|||
|
|||
_setContrast = function(m, n) { |
|||
return _applyMatrix([n,0,0,0,0.5 * (1 - n), 0,n,0,0,0.5 * (1 - n), 0,0,n,0,0.5 * (1 - n), 0,0,0,1,0], m); |
|||
}, |
|||
|
|||
_getFilter = function(t, type) { |
|||
var filterClass = _gsScope.PIXI.filters[type], |
|||
filters = t.filters || [], |
|||
i = filters.length, |
|||
filter; |
|||
if (!filterClass) { |
|||
throw("PixiPlugin error: " + type + " isn't present."); |
|||
} |
|||
while (--i > -1) { |
|||
if (filters[i] instanceof filterClass) { |
|||
return filters[i]; |
|||
} |
|||
} |
|||
filter = new filterClass(); |
|||
if (type === "BlurFilter") { |
|||
filter.blur = 0; |
|||
} |
|||
filters.push(filter); |
|||
t.filters = filters; |
|||
return filter; |
|||
}, |
|||
|
|||
_addColorMatrixFilterCacheTween = function(p, pg, cache, vars) { //we cache the ColorMatrixFilter components in a _gsColorMatrixFilter object attached to the target object so that it's easy to grab the current value at any time.
|
|||
pg._addTween(cache, p, cache[p], vars[p], p); |
|||
pg._overwriteProps.push(p); |
|||
}, |
|||
|
|||
_applyBrightnessToMatrix = function(brightness, matrix) { |
|||
var temp = new _gsScope.PIXI.filters.ColorMatrixFilter(); |
|||
temp.matrix = matrix; |
|||
temp.brightness(brightness, true); |
|||
return temp.matrix; |
|||
}, |
|||
|
|||
_CMFdefaults = {contrast:1, saturation:1, colorizeAmount:0, colorize:"rgb(255,255,255)", hue:0, brightness:1}, |
|||
|
|||
_parseColorMatrixFilter = function(t, v, pg) { |
|||
var filter = _getFilter(t, "ColorMatrixFilter"), |
|||
cache = t._gsColorMatrixFilter = t._gsColorMatrixFilter || {contrast:1, saturation:1, colorizeAmount:0, colorize:"rgb(255,255,255)", hue:0, brightness:1}, |
|||
combine = v.combineCMF && !("colorMatrixFilter" in v && !v.colorMatrixFilter), |
|||
i, matrix, startMatrix; |
|||
startMatrix = filter.matrix; |
|||
if (v.matrix && v.matrix.length === startMatrix.length) { |
|||
matrix = v.matrix; |
|||
if (cache.contrast !== 1) { |
|||
_addColorMatrixFilterCacheTween("contrast", pg, cache, _CMFdefaults); |
|||
} |
|||
if (cache.hue) { |
|||
_addColorMatrixFilterCacheTween("hue", pg, cache, _CMFdefaults); |
|||
} |
|||
if (cache.brightness !== 1) { |
|||
_addColorMatrixFilterCacheTween("brightness", pg, cache, _CMFdefaults); |
|||
} |
|||
if (cache.colorizeAmount) { |
|||
_addColorMatrixFilterCacheTween("colorize", pg, cache, _CMFdefaults); |
|||
_addColorMatrixFilterCacheTween("colorizeAmount", pg, cache, _CMFdefaults); |
|||
} |
|||
if (cache.saturation !== 1) { |
|||
_addColorMatrixFilterCacheTween("saturation", pg, cache, _CMFdefaults); |
|||
} |
|||
|
|||
} else { |
|||
matrix = _idMatrix.slice(); |
|||
if (v.contrast != null) { |
|||
matrix = _setContrast(matrix, Number(v.contrast)); |
|||
_addColorMatrixFilterCacheTween("contrast", pg, cache, v); |
|||
} else if (cache.contrast !== 1) { |
|||
if (combine) { |
|||
matrix = _setContrast(matrix, cache.contrast); |
|||
} else { |
|||
_addColorMatrixFilterCacheTween("contrast", pg, cache, _CMFdefaults); |
|||
} |
|||
} |
|||
if (v.hue != null) { |
|||
matrix = _setHue(matrix, Number(v.hue)); |
|||
_addColorMatrixFilterCacheTween("hue", pg, cache, v); |
|||
} else if (cache.hue) { |
|||
if (combine) { |
|||
matrix = _setHue(matrix, cache.hue); |
|||
} else { |
|||
_addColorMatrixFilterCacheTween("hue", pg, cache, _CMFdefaults); |
|||
} |
|||
} |
|||
if (v.brightness != null) { |
|||
matrix = _applyBrightnessToMatrix(Number(v.brightness), matrix); |
|||
_addColorMatrixFilterCacheTween("brightness", pg, cache, v); |
|||
} else if (cache.brightness !== 1) { |
|||
if (combine) { |
|||
matrix = _applyBrightnessToMatrix(cache.brightness, matrix); |
|||
} else { |
|||
_addColorMatrixFilterCacheTween("brightness", pg, cache, _CMFdefaults); |
|||
} |
|||
} |
|||
if (v.colorize != null) { |
|||
v.colorizeAmount = ("colorizeAmount" in v) ? Number(v.colorizeAmount) : 1; |
|||
matrix = _colorize(matrix, v.colorize, v.colorizeAmount); |
|||
_addColorMatrixFilterCacheTween("colorize", pg, cache, v); |
|||
_addColorMatrixFilterCacheTween("colorizeAmount", pg, cache, v); |
|||
} else if (cache.colorizeAmount) { |
|||
if (combine) { |
|||
matrix = _colorize(matrix, cache.colorize, cache.colorizeAmount); |
|||
} else { |
|||
_addColorMatrixFilterCacheTween("colorize", pg, cache, _CMFdefaults); |
|||
_addColorMatrixFilterCacheTween("colorizeAmount", pg, cache, _CMFdefaults); |
|||
} |
|||
} |
|||
if (v.saturation != null) { |
|||
matrix = _setSaturation(matrix, Number(v.saturation)); |
|||
_addColorMatrixFilterCacheTween("saturation", pg, cache, v); |
|||
} else if (cache.saturation !== 1) { |
|||
if (combine) { |
|||
matrix = _setSaturation(matrix, cache.saturation); |
|||
} else { |
|||
_addColorMatrixFilterCacheTween("saturation", pg, cache, _CMFdefaults); |
|||
} |
|||
} |
|||
} |
|||
i = matrix.length; |
|||
while (--i > -1) { |
|||
if (matrix[i] !== startMatrix[i]) { |
|||
pg._addTween(startMatrix, i, startMatrix[i], matrix[i], "colorMatrixFilter"); |
|||
} |
|||
} |
|||
pg._overwriteProps.push("colorMatrixFilter"); |
|||
}, |
|||
|
|||
_addColorTween = function(target, p, value, colorSetter, plugin) { |
|||
var pt = colorSetter._firstPT = {_next:colorSetter._firstPT, t:target, p:p, proxy:{}, f:(typeof(target[p]) === "function")}; |
|||
pt.proxy[p] = "rgb(" + _parseColor(!pt.f ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]()).join(",") + ")"; |
|||
plugin._addTween(pt.proxy, p, "get", ((typeof(value) === "number") ? "rgb(" + _parseColor(value, false).join(",") + ")" : value), p, null, null, _colorStringFilter); |
|||
}, |
|||
|
|||
//to improve performance, when a color is sensed, we hijack the setRatio() method of the plugin instance with a new function that this method spits back. This is a special method that handles parsing color values on-the-fly and turns them into numeric values which PixiJS requires. In other words, instead of "rgb(255, 0, 0)", PixiJS wants 0xFF0000. This also works with hsl() values.
|
|||
_buildColorSetter = function(tween, plugin) { |
|||
var setRatio = plugin.setRatio, //save the original (super) setRatio() function
|
|||
func = function(v) { |
|||
var pt = func._firstPT, |
|||
val; |
|||
setRatio.call(plugin, v); |
|||
while (pt) { |
|||
val = _parseColor(pt.proxy[pt.p], "number"); |
|||
if (pt.f) { |
|||
pt.t[pt.p](val); |
|||
} else { |
|||
pt.t[pt.p] = val; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
if (func.graphics) { //in order for PixiJS to actually redraw GraphicsData, we've gotta increment the "dirty" and "clearDirty" values. If we don't do this, the values will be tween properly, but not rendered.
|
|||
func.graphics.dirty++; |
|||
func.graphics.clearDirty++; |
|||
} |
|||
}; |
|||
plugin.setRatio = func; |
|||
return func; |
|||
}, |
|||
|
|||
|
|||
_colorProps = {tint:1, lineColor:1, fillColor:1}, |
|||
_xyContexts = "position,scale,skew,pivot,anchor,tilePosition,tileScale".split(","), |
|||
_contexts = {x:"position", y:"position", tileX:"tilePosition", tileY:"tilePosition"}, |
|||
_colorMatrixFilterProps = {colorMatrixFilter:1, saturation:1, contrast:1, hue:1, colorize:1, colorizeAmount:1, brightness:1, combineCMF:1}, |
|||
_DEG2RAD = Math.PI / 180, |
|||
_degreesToRadians = function(value) { |
|||
return (typeof(value) === "string" && value.charAt(1) === "=") ? value.substr(0, 2) + (parseFloat(value.substr(2)) * _DEG2RAD) : value * _DEG2RAD; |
|||
}, i, p; |
|||
|
|||
//context setup...
|
|||
for (i = 0; i < _xyContexts.length; i++) { |
|||
p = _xyContexts[i]; |
|||
_contexts[p + "X"] = p; |
|||
_contexts[p + "Y"] = p; |
|||
} |
|||
|
|||
//color parsing setup...
|
|||
for (p in _colorLookup) { |
|||
_colorExp += "|" + p + "\\b"; |
|||
} |
|||
_colorExp = new RegExp(_colorExp+")", "gi"); |
|||
_colorStringFilter = function(a) { |
|||
var combined = a[0] + " " + a[1], |
|||
toHSL; |
|||
_colorExp.lastIndex = 0; |
|||
if (_colorExp.test(combined)) { |
|||
toHSL = (combined.indexOf("hsl(") !== -1 || combined.indexOf("hsla(") !== -1); |
|||
a[0] = _formatColors(a[0], toHSL); |
|||
a[1] = _formatColors(a[1], toHSL); |
|||
} |
|||
}; |
|||
|
|||
if (!TweenLite.defaultStringFilter) { |
|||
TweenLite.defaultStringFilter = _colorStringFilter; |
|||
} |
|||
|
|||
var PixiPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "pixi", |
|||
priority: 0, |
|||
API: 2, |
|||
global: true, |
|||
version: "0.1.2", |
|||
|
|||
init: function (target, values, tween, index) { |
|||
if (!target instanceof _gsScope.PIXI.DisplayObject) { |
|||
return false; |
|||
} |
|||
var context, axis, value, colorMatrix, filter, p, padding, colorSetter, i, data; |
|||
for (p in values) { |
|||
context = _contexts[p]; |
|||
value = values[p]; |
|||
if (typeof(value) === "function") { |
|||
value = value(index || 0, target); |
|||
} |
|||
if (context) { |
|||
axis = (p.charAt(p.length-1).toLowerCase().indexOf("x") !== -1) ? "x" : "y"; |
|||
this._addTween(target[context], axis, target[context][axis], (context === "skew") ? _degreesToRadians(value) : value, p); |
|||
} else if (p === "scale" || p === "anchor" || p === "pivot" || p === "tileScale") { |
|||
this._addTween(target[p], "x", target[p].x, value, p + "X"); |
|||
this._addTween(target[p], "y", target[p].y, value, p + "Y"); |
|||
} else if (p === "rotation") { //PIXI expects rotation in radians, but as a convenience we let folks define it in degrees and we do the conversion.
|
|||
this._addTween(target, p, target.rotation, _degreesToRadians(value), p); |
|||
|
|||
} else if (_colorMatrixFilterProps[p]) { |
|||
if (!colorMatrix) { |
|||
_parseColorMatrixFilter(target, values.colorMatrixFilter || values, this); |
|||
colorMatrix = true; |
|||
} |
|||
} else if (p === "blur" || p === "blurX" || p === "blurY" || p === "blurPadding") { |
|||
filter = _getFilter(target, "BlurFilter"); |
|||
this._addTween(filter, p, filter[p], value, p); |
|||
if (values.blurPadding !== 0) { |
|||
padding = values.blurPadding || Math.max(filter[p], value) * 2; |
|||
i = target.filters.length; |
|||
while (--i > -1) { |
|||
target.filters[i].padding = Math.max(target.filters[i].padding, padding); //if we don't expand the padding on all the filters, it can look clipped.
|
|||
} |
|||
} |
|||
} else if (_colorProps[p]) { |
|||
if (!colorSetter) { |
|||
colorSetter = _buildColorSetter(tween, this); |
|||
} |
|||
if ((p === "lineColor" || p === "fillColor") && target instanceof _gsScope.PIXI.Graphics) { |
|||
data = target.graphicsData; |
|||
i = data.length; |
|||
while (--i > -1) { |
|||
_addColorTween(data[i], p, value, colorSetter, this); |
|||
} |
|||
colorSetter.graphics = target; |
|||
} else { |
|||
_addColorTween(target, p, value, colorSetter, this); |
|||
} |
|||
} else { |
|||
this._addTween(target, p, target[p], value, p); |
|||
} |
|||
this._overwriteProps.push(p); |
|||
} |
|||
return true; |
|||
} |
|||
}); |
|||
|
|||
PixiPlugin.colorProps = _colorProps; |
|||
PixiPlugin.parseColor = _parseColor; |
|||
PixiPlugin.formatColors = _formatColors; |
|||
PixiPlugin.colorStringFilter = _colorStringFilter; |
|||
|
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("PixiPlugin")); |
|||
@ -0,0 +1,369 @@ |
|||
/*! |
|||
* VERSION: 0.2.2 |
|||
* DATE: 2017-01-17 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _NaNExp = /[^\d\-\.]/g, |
|||
_DEG2RAD = Math.PI / 180, |
|||
_numExp = /(\d|\.)+/g, |
|||
_colorLookup = {aqua:[0,255,255], |
|||
lime:[0,255,0], |
|||
silver:[192,192,192], |
|||
black:[0,0,0], |
|||
maroon:[128,0,0], |
|||
teal:[0,128,128], |
|||
blue:[0,0,255], |
|||
navy:[0,0,128], |
|||
white:[255,255,255], |
|||
fuchsia:[255,0,255], |
|||
olive:[128,128,0], |
|||
yellow:[255,255,0], |
|||
orange:[255,165,0], |
|||
gray:[128,128,128], |
|||
purple:[128,0,128], |
|||
green:[0,128,0], |
|||
red:[255,0,0], |
|||
pink:[255,192,203], |
|||
cyan:[0,255,255], |
|||
transparent:[255,255,255,0]}, |
|||
//parses a color (like #9F0, #FF9900, or rgb(255,51,153)) into an array with 3 elements for red, green, and blue. Also handles rgba() values (splits into array of 4 elements of course)
|
|||
_parseColor = function(color) { |
|||
if (typeof(color) === "number") { |
|||
return [color >> 16, (color >> 8) & 255, color & 255]; |
|||
} else if (color === "" || color == null || color === "none" || typeof(color) !== "string") { |
|||
return _colorLookup.transparent; |
|||
} else if (_colorLookup[color]) { |
|||
return _colorLookup[color]; |
|||
} else if (color.charAt(0) === "#") { |
|||
if (color.length === 4) { //for shorthand like #9F0
|
|||
color = "#" + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2) + color.charAt(3) + color.charAt(3); |
|||
} |
|||
color = parseInt(color.substr(1), 16); |
|||
return [color >> 16, (color >> 8) & 255, color & 255]; |
|||
} |
|||
return color.match(_numExp) || _colorLookup.transparent; |
|||
}, |
|||
|
|||
_transformMap = {scaleX:1, scaleY:1, tx:1, ty:1, rotation:1, shortRotation:1, skewX:1, skewY:1, scale:1}, |
|||
|
|||
//parses the transform values for an element, returning an object with x, y, scaleX, scaleY, rotation, skewX, and skewY properties. Note: by default (for performance reasons), all skewing is combined into skewX and rotation but skewY still has a place in the transform object so that we can record how much of the skew is attributed to skewX vs skewY. Remember, a skewY of 10 looks the same as a rotation of 10 and skewX of -10.
|
|||
_getTransform = function(t, rec) { |
|||
var s = t.matrix, |
|||
min = 0.000001, |
|||
a = s.a, |
|||
b = s.b, |
|||
c = s.c, |
|||
d = s.d, |
|||
m = rec ? t._gsTransform || {skewY:0} : {skewY:0}, |
|||
invX = (m.scaleX < 0); //in order to interpret things properly, we need to know if the user applied a negative scaleX previously so that we can adjust the rotation and skewX accordingly. Otherwise, if we always interpret a flipped matrix as affecting scaleY and the user only wants to tween the scaleX on multiple sequential tweens, it would keep the negative scaleY without that being the user's intent.
|
|||
|
|||
m.tx = s.e - (m.ox || 0); //ox is the offset x that we record in setRatio() whenever we apply a custom transform that might use a pivot point. Remember, s.e and s.f get affected by things like scale. For example, imagine an object whose top left corner is at 100,100 and then we scale it up to 300% using the center as the pivot point - that corner would now be very different even though to the user, they didn't intend to change/tween the x/y position per se. Therefore, we record whatever offsets we make so that we can compensate when reading the values back.
|
|||
m.ty = s.f - (m.oy || 0); //oy is the offset y (see note above)
|
|||
m.scaleX = Math.sqrt(a * a + b * b); |
|||
m.scaleY = Math.sqrt(d * d + c * c); |
|||
m.rotation = (a || b) ? Math.atan2(b, a) : m.rotation || 0; //note: if scaleX is 0, we cannot accurately measure rotation. Same for skewX with a scaleY of 0. Therefore, we default to the previously recorded value (or zero if that doesn't exist).
|
|||
m.skewX = (c || d) ? Math.atan2(c, d) + m.rotation : m.skewX || 0; |
|||
if (Math.abs(m.skewX) > Math.PI / 2) { |
|||
if (invX) { |
|||
m.scaleX *= -1; |
|||
m.skewX += (m.rotation <= 0) ? Math.PI : -Math.PI; |
|||
m.rotation += (m.rotation <= 0) ? Math.PI : -Math.PI; |
|||
} else { |
|||
m.scaleY *= -1; |
|||
m.skewX += (m.skewX <= 0) ? Math.PI : -Math.PI; |
|||
} |
|||
} |
|||
//some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 0 in these cases. The conditional logic here is faster than calling Math.abs().
|
|||
if (m.rotation < min) if (m.rotation > -min) if (a || b) { |
|||
m.rotation = 0; |
|||
} |
|||
if (m.skewX < min) if (m.skewX > -min) if (b || c) { |
|||
m.skewX = 0; |
|||
} |
|||
if (rec) { |
|||
t._gsTransform = m; //record to the object's _gsTransform which we use so that tweens can control individual properties independently (we need all the properties to accurately recompose the matrix in the setRatio() method)
|
|||
} |
|||
return m; |
|||
}, |
|||
|
|||
//takes a value and a default number, checks if the value is relative, null, or numeric and spits back a normalized number accordingly. Primarily used in the _parseTransform() function.
|
|||
_parseVal = function(v, d) { |
|||
return (v == null) ? d : (typeof(v) === "string" && v.indexOf("=") === 1) ? parseInt(v.charAt(0)+"1", 10) * Number(v.substr(2)) + d : Number(v); |
|||
}, |
|||
|
|||
//translates strings like "40deg" or "40" or 40rad" or "+=40deg" to a numeric radian angle, optionally relative to a default value (if "+=" or "-=" prefix is found)
|
|||
_parseAngle = function(v, d) { |
|||
var m = (v.indexOf("rad") === -1) ? _DEG2RAD : 1, |
|||
r = (v.indexOf("=") === 1); |
|||
v = Number(v.replace(_NaNExp, "")) * m; |
|||
return r ? v + d : v; |
|||
}, |
|||
|
|||
|
|||
RaphaelPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "raphael", |
|||
version: "0.2.2", |
|||
API: 2, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween) { |
|||
if (!target.attr) { //raphael must have attr() method
|
|||
return false; |
|||
} |
|||
this._target = target; |
|||
this._tween = tween; |
|||
this._props = target._gsProps = target._gsProps || {}; |
|||
var p, s, v, pt, clr1, clr2, rel; |
|||
|
|||
for (p in value) { |
|||
|
|||
v = value[p]; |
|||
|
|||
if (p === "transform") { |
|||
this._parseTransform(target, v); |
|||
continue; |
|||
} else if (_transformMap[p] || p === "pivot") { |
|||
this._parseTransform(target, value); |
|||
continue; |
|||
} |
|||
|
|||
s = target.attr(p); |
|||
|
|||
//Some of these properties are in place in order to conform with the standard PropTweens in TweenPlugins so that overwriting and roundProps occur properly. For example, f and r may seem unnecessary here, but they enable other functionality.
|
|||
//_next:* next linked list node [object]
|
|||
//t: * target [object]
|
|||
//p: * property (camelCase) [string]
|
|||
//s: * starting value [number]
|
|||
//c: * change value [number]
|
|||
//f: * is function [boolean]
|
|||
//n: * name (for overwriting) [string]
|
|||
//b: beginning value [string]
|
|||
//i: intermediate value [string]
|
|||
//e: ending value [string]
|
|||
//r: * round [boolean]
|
|||
//type: 0=normal, 1=color, 2=rgba, -1=non-tweening prop [number]
|
|||
this._firstPT = pt = {_next:this._firstPT, |
|||
t:this._props, |
|||
p:p, |
|||
b:s, |
|||
f:false, |
|||
n:"raphael_" + p, |
|||
r:false, |
|||
type:0}; |
|||
|
|||
//color values must be split apart into their R, G, B (and sometimes alpha) values and tweened independently.
|
|||
if (p === "fill" || p === "stroke") { |
|||
clr1 = _parseColor(s); |
|||
clr2 = _parseColor(v); |
|||
pt.e = v; |
|||
pt.s = Number(clr1[0]); //red starting value
|
|||
pt.c = Number(clr2[0]) - pt.s; //red change
|
|||
pt.gs = Number(clr1[1]); //green starting value
|
|||
pt.gc = Number(clr2[1]) - pt.gs; //green change
|
|||
pt.bs = Number(clr1[2]); //blue starting value
|
|||
pt.bc = Number(clr2[2]) - pt.bs; //blue change
|
|||
if (clr1.length > 3 || clr2.length > 3) { //detect an rgba() value
|
|||
pt.as = (clr1.length < 4) ? 1 : Number(clr1[3]); |
|||
pt.ac = ((clr2.length < 4) ? 1 : Number(clr2[3])) - pt.as; |
|||
pt.type = 2; //2 = rgba() tween
|
|||
} else { |
|||
pt.type = 1; //1 = color tween, -1 = no tween, just set the value at the end because there's no changes
|
|||
} |
|||
|
|||
} else { |
|||
|
|||
s = (typeof(s) === "string") ? parseFloat(s.replace(_NaNExp, "")) : Number(s); |
|||
|
|||
if (typeof(v) === "string") { |
|||
rel = (v.charAt(1) === "="); |
|||
v = parseFloat(v.replace(_NaNExp, "")); |
|||
} else { |
|||
rel = false; |
|||
} |
|||
|
|||
pt.e = (v || v === 0) ? (rel ? v + s : v) : value[p]; //ensures that any += or -= prefixes are taken care of.
|
|||
|
|||
if ((s || s === 0) && (v || v === 0) && (pt.c = (rel ? v : v - s))) { //faster than isNaN(). Also, we set pt.c (change) here because if it's 0, we'll just treat it like a non-tweening value. can't do (v !== start) because if it's a relative value and the CHANGE is identical to the START, the condition will fail unnecessarily.
|
|||
pt.s = s; |
|||
} else { |
|||
pt.type = -1; |
|||
pt.i = value[p]; //intermediate value is typically the same as the end value.
|
|||
pt.s = pt.c = 0; |
|||
} |
|||
|
|||
} |
|||
|
|||
this._overwriteProps.push("raphael_" + p); |
|||
if (pt._next) { |
|||
pt._next._prev = pt; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(v) { |
|||
var pt = this._firstPT, val; |
|||
|
|||
while (pt) { |
|||
val = pt.c * v + pt.s; |
|||
if (pt.r) { |
|||
val = Math.round(val); |
|||
} |
|||
if (!pt.type) { |
|||
pt.t[pt.p] = val; |
|||
} else if (pt.type === 1) { //rgb()
|
|||
pt.t[pt.p] = "rgb(" + (val >> 0) + ", " + ((pt.gs + (v * pt.gc)) >> 0) + ", " + ((pt.bs + (v * pt.bc)) >> 0) + ")"; |
|||
} else if (pt.type === 2) { //rgba()
|
|||
pt.t[pt.p] = "rgba(" + (val >> 0) + ", " + ((pt.gs + (v * pt.gc)) >> 0) + ", " + ((pt.bs + (v * pt.bc)) >> 0) + ", " + (pt.as + (v * pt.ac)) + ")"; |
|||
} else if (pt.type === -1) { //non-tweening
|
|||
pt.t[pt.p] = pt.i; |
|||
} |
|||
pt = pt._next; |
|||
} |
|||
|
|||
this._target.attr(this._props); |
|||
|
|||
//apply transform values like x, y, scaleX, scaleY, rotation, skewX, or skewY. We do these after looping through all the PropTweens because those are where the changes are made to scaleX/scaleY/rotation/skewX/skewY/x/y.
|
|||
if (this._transform) { |
|||
pt = this._transform; //to improve speed and reduce size, reuse the pt variable as an alias to the _transform property
|
|||
var ang = pt.rotation, |
|||
skew = ang - pt.skewX, |
|||
a = Math.cos(ang) * pt.scaleX, |
|||
b = Math.sin(ang) * pt.scaleX, |
|||
c = Math.sin(skew) * -pt.scaleY, |
|||
d = Math.cos(skew) * pt.scaleY, |
|||
min = 0.000001, |
|||
pxl = this._pxl, |
|||
pyl = this._pyl; |
|||
|
|||
//some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 0 in these cases for both b and c. The conditional logic here is faster than calling Math.abs().
|
|||
if (b < min) if (b > -min) { |
|||
b = 0; |
|||
} |
|||
if (c < min) if (c > -min) { |
|||
c = 0; |
|||
} |
|||
pt.ox = this._pxg - (pxl * a + pyl * c); //we must record the offset x/y that we're making from the regular tx/ty (matrix.e and f) so that we can correctly interpret positional data in _getTransform(). See note there on tx and ox.
|
|||
pt.oy = this._pyg - (pxl * b + pyl * d); |
|||
this._target.transform("m" + a + "," + b + "," + c + "," + d + "," + (pt.tx + pt.ox) + "," + (pt.ty + pt.oy)); |
|||
} |
|||
|
|||
} |
|||
|
|||
}), |
|||
p = RaphaelPlugin.prototype; |
|||
|
|||
//compares the beginning x, y, scaleX, scaleY, rotation, and skewX properties with the ending ones and adds PropTweens accordingly wherever necessary. We must tween them individually (rather than just tweening the matrix values) so that elgant overwriting can occur, like if one tween is controlling scaleX, scaleY, and rotation and then another one starts mid-tween that is trying to control the scaleX only - this tween should continue tweening scaleY and rotation.
|
|||
p._parseTransform = function(t, v) { |
|||
if (this._transform) { return; } //only need to parse the transform once, and only if the browser supports it.
|
|||
|
|||
var m1 = this._transform = _getTransform(t, true), |
|||
min = 0.000001, |
|||
m2, skewY, p, pt, copy, dx, dy, mtx, pivot; |
|||
|
|||
if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
|
|||
|
|||
m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX), |
|||
scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY), |
|||
tx:_parseVal(v.tx, m1.tx), |
|||
ty:_parseVal(v.ty, m1.ty)}; |
|||
|
|||
if (v.shortRotation != null) { |
|||
m2.rotation = (typeof(v.shortRotation) === "number") ? v.shortRotation * _DEG2RAD : _parseAngle(v.shortRotation, m1.rotation); |
|||
var dif = (m2.rotation - m1.rotation) % (Math.PI * 2); |
|||
if (dif !== dif % Math.PI) { |
|||
dif += Math.PI * ((dif < 0) ? 2 : -2); |
|||
} |
|||
m2.rotation = m1.rotation + dif; |
|||
|
|||
} else { |
|||
m2.rotation = (v.rotation == null) ? m1.rotation : (typeof(v.rotation) === "number") ? v.rotation * _DEG2RAD : _parseAngle(v.rotation, m1.rotation); |
|||
} |
|||
m2.skewX = (v.skewX == null) ? m1.skewX : (typeof(v.skewX) === "number") ? v.skewX * _DEG2RAD : _parseAngle(v.skewX, m1.skewX); |
|||
|
|||
//note: for performance reasons, we combine all skewing into the skewX and rotation values, ignoring skewY but we must still record it so that we can discern how much of the overall skew is attributed to skewX vs. skewY. Otherwise, if the skewY would always act relative (tween skewY to 10deg, for example, multiple times and if we always combine things into skewX, we can't remember that skewY was 10 from last time). Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of -10 degrees.
|
|||
m2.skewY = (v.skewY == null) ? m1.skewY : (typeof(v.skewY) === "number") ? v.skewY * _DEG2RAD : _parseAngle(v.skewY, m1.skewY); |
|||
if ((skewY = m2.skewY - m1.skewY)) { |
|||
m2.skewX += skewY; |
|||
m2.rotation += skewY; |
|||
} |
|||
//don't allow rotation/skew values to be a SUPER small decimal because when they're translated back to strings for setting the css property, the browser reports them in a funky way, like 1-e7. Of course we could use toFixed() to resolve that issue but that hurts performance quite a bit with all those function calls on every frame, plus it is virtually impossible to discern values that small visually (nobody will notice changing a rotation of 0.0000001 to 0, so the performance improvement is well worth it).
|
|||
if (m2.skewY < min) if (m2.skewY > -min) { |
|||
m2.skewY = 0; |
|||
} |
|||
if (m2.skewX < min) if (m2.skewX > -min) { |
|||
m2.skewX = 0; |
|||
} |
|||
if (m2.rotation < min) if (m2.rotation > -min) { |
|||
m2.rotation = 0; |
|||
} |
|||
|
|||
pivot = v.localPivot || v.globalPivot; |
|||
|
|||
if (typeof(pivot) === "string") { |
|||
copy = pivot.split(","); |
|||
dx = Number(copy[0]); |
|||
dy = Number(copy[1]); |
|||
} else if (typeof(pivot) === "object") { |
|||
dx = Number(pivot.x); |
|||
dy = Number(pivot.y); |
|||
} else if (v.localPivot) { |
|||
copy = t.getBBox(true); |
|||
dx = copy.width / 2; |
|||
dy = copy.height / 2; |
|||
} else { |
|||
copy = t.getBBox(); |
|||
dx = copy.x + copy.width / 2; |
|||
dy = copy.y + copy.height / 2; |
|||
} |
|||
|
|||
if (v.localPivot) { |
|||
mtx = t.matrix; |
|||
dx += t.attr("x"); |
|||
dy += t.attr("y"); |
|||
this._pxl = dx; |
|||
this._pyl = dy; |
|||
this._pxg = dx * mtx.a + dy * mtx.c + mtx.e - m1.tx; |
|||
this._pyg = dx * mtx.b + dy * mtx.d + mtx.f - m1.ty; |
|||
} else { |
|||
mtx = t.matrix.invert(); |
|||
this._pxl = dx * mtx.a + dy * mtx.c + mtx.e; |
|||
this._pyl = dx * mtx.b + dy * mtx.d + mtx.f; |
|||
this._pxg = dx - m1.tx; |
|||
this._pyg = dy - m1.ty; |
|||
} |
|||
|
|||
} else if (typeof(v) === "string") { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
|
|||
copy = this._target.transform(); |
|||
t.transform(v); |
|||
m2 = _getTransform(t, false); |
|||
t.transform(copy); |
|||
} else { |
|||
return; |
|||
} |
|||
|
|||
for (p in _transformMap) { |
|||
if (m1[p] !== m2[p]) if (p !== "shortRotation") if (p !== "scale") { |
|||
this._firstPT = pt = {_next:this._firstPT, t:m1, p:p, s:m1[p], c:m2[p] - m1[p], n:p, f:false, r:false, b:m1[p], e:m2[p], type:0}; |
|||
if (pt._next) { |
|||
pt._next._prev = pt; |
|||
} |
|||
this._overwriteProps.push("raphael_" + p); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
@ -0,0 +1,87 @@ |
|||
/*! |
|||
* VERSION: 1.6.0 |
|||
* DATE: 2017-01-17 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var RoundPropsPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "roundProps", |
|||
version: "1.6.0", |
|||
priority: -1, |
|||
API: 2, |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween) { |
|||
this._tween = tween; |
|||
return true; |
|||
} |
|||
|
|||
}), |
|||
_roundLinkedList = function(node) { |
|||
while (node) { |
|||
if (!node.f && !node.blob) { |
|||
node.m = Math.round; |
|||
} |
|||
node = node._next; |
|||
} |
|||
}, |
|||
p = RoundPropsPlugin.prototype; |
|||
|
|||
p._onInitAllProps = function() { |
|||
var tween = this._tween, |
|||
rp = (tween.vars.roundProps.join) ? tween.vars.roundProps : tween.vars.roundProps.split(","), |
|||
i = rp.length, |
|||
lookup = {}, |
|||
rpt = tween._propLookup.roundProps, |
|||
prop, pt, next; |
|||
while (--i > -1) { |
|||
lookup[rp[i]] = Math.round; |
|||
} |
|||
i = rp.length; |
|||
while (--i > -1) { |
|||
prop = rp[i]; |
|||
pt = tween._firstPT; |
|||
while (pt) { |
|||
next = pt._next; //record here, because it may get removed
|
|||
if (pt.pg) { |
|||
pt.t._mod(lookup); |
|||
} else if (pt.n === prop) { |
|||
if (pt.f === 2 && pt.t) { //a blob (text containing multiple numeric values)
|
|||
_roundLinkedList(pt.t._firstPT); |
|||
} else { |
|||
this._add(pt.t, prop, pt.s, pt.c); |
|||
//remove from linked list
|
|||
if (next) { |
|||
next._prev = pt._prev; |
|||
} |
|||
if (pt._prev) { |
|||
pt._prev._next = next; |
|||
} else if (tween._firstPT === pt) { |
|||
tween._firstPT = next; |
|||
} |
|||
pt._next = pt._prev = null; |
|||
tween._propLookup[prop] = rpt; |
|||
} |
|||
} |
|||
pt = next; |
|||
} |
|||
} |
|||
return false; |
|||
}; |
|||
|
|||
p._add = function(target, p, s, c) { |
|||
this._addTween(target, p, s, s + c, p, Math.round); |
|||
this._overwriteProps.push(p); |
|||
}; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
@ -0,0 +1,183 @@ |
|||
/*! |
|||
* VERSION: 1.9.0 |
|||
* DATE: 2017-06-19 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _doc = (_gsScope.document || {}).documentElement, |
|||
_window = _gsScope, |
|||
_max = function(element, axis) { |
|||
var dim = (axis === "x") ? "Width" : "Height", |
|||
scroll = "scroll" + dim, |
|||
client = "client" + dim, |
|||
body = document.body; |
|||
return (element === _window || element === _doc || element === body) ? Math.max(_doc[scroll], body[scroll]) - (_window["inner" + dim] || _doc[client] || body[client]) : element[scroll] - element["offset" + dim]; |
|||
}, |
|||
_unwrapElement = function(value) { |
|||
if (typeof(value) === "string") { |
|||
value = TweenLite.selector(value); |
|||
} |
|||
if (value.length && value !== _window && value[0] && value[0].style && !value.nodeType) { |
|||
value = value[0]; |
|||
} |
|||
return (value === _window || (value.nodeType && value.style)) ? value : null; |
|||
}, |
|||
_buildGetter = function(e, axis) { //pass in an element and an axis ("x" or "y") and it'll return a getter function for the scroll position of that element (like scrollTop or scrollLeft, although if the element is the window, it'll use the pageXOffset/pageYOffset or the documentElement's scrollTop/scrollLeft or document.body's. Basically this streamlines things and makes a very fast getter across browsers.
|
|||
var p = "scroll" + ((axis === "x") ? "Left" : "Top"); |
|||
if (e === _window) { |
|||
if (e.pageXOffset != null) { |
|||
p = "page" + axis.toUpperCase() + "Offset"; |
|||
} else if (_doc[p] != null) { |
|||
e = _doc; |
|||
} else { |
|||
e = document.body; |
|||
} |
|||
} |
|||
return function() { |
|||
return e[p]; |
|||
}; |
|||
}, |
|||
_getOffset = function(element, container) { |
|||
var rect = _unwrapElement(element).getBoundingClientRect(), |
|||
isRoot = (!container || container === _window || container === document.body), |
|||
cRect = (isRoot ? _doc : container).getBoundingClientRect(), |
|||
offsets = {x: rect.left - cRect.left, y: rect.top - cRect.top}; |
|||
if (!isRoot && container) { //only add the current scroll position if it's not the window/body.
|
|||
offsets.x += _buildGetter(container, "x")(); |
|||
offsets.y += _buildGetter(container, "y")(); |
|||
} |
|||
return offsets; |
|||
}, |
|||
_parseVal = function(value, target, axis) { |
|||
var type = typeof(value); |
|||
return !isNaN(value) ? parseFloat(value) : (type === "number" || (type === "string" && value.charAt(1) === "=")) ? value : (value === "max") ? _max(target, axis) : Math.min(_max(target, axis), _getOffset(value, target)[axis]); |
|||
}, |
|||
|
|||
ScrollToPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "scrollTo", |
|||
API: 2, |
|||
global: true, |
|||
version:"1.9.0", |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween) { |
|||
this._wdw = (target === _window); |
|||
this._target = target; |
|||
this._tween = tween; |
|||
if (typeof(value) !== "object") { |
|||
value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
|
|||
if (typeof(value.y) === "string" && value.y !== "max" && value.y.charAt(1) !== "=") { |
|||
value.x = value.y; |
|||
} |
|||
} else if (value.nodeType) { |
|||
value = {y:value, x:value}; |
|||
} |
|||
this.vars = value; |
|||
this._autoKill = (value.autoKill !== false); |
|||
this.getX = _buildGetter(target, "x"); |
|||
this.getY = _buildGetter(target, "y"); |
|||
this.x = this.xPrev = this.getX(); |
|||
this.y = this.yPrev = this.getY(); |
|||
if (value.x != null) { |
|||
this._addTween(this, "x", this.x, _parseVal(value.x, target, "x") - (value.offsetX || 0), "scrollTo_x", true); |
|||
this._overwriteProps.push("scrollTo_x"); |
|||
} else { |
|||
this.skipX = true; |
|||
} |
|||
if (value.y != null) { |
|||
this._addTween(this, "y", this.y, _parseVal(value.y, target, "y") - (value.offsetY || 0), "scrollTo_y", true); |
|||
this._overwriteProps.push("scrollTo_y"); |
|||
} else { |
|||
this.skipY = true; |
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(v) { |
|||
this._super.setRatio.call(this, v); |
|||
|
|||
var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev, |
|||
y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev, |
|||
yDif = y - this.yPrev, |
|||
xDif = x - this.xPrev, |
|||
threshold = ScrollToPlugin.autoKillThreshold; |
|||
|
|||
if (this.x < 0) { //can't scroll to a position less than 0! Might happen if someone uses a Back.easeOut or Elastic.easeOut when scrolling back to the top of the page (for example)
|
|||
this.x = 0; |
|||
} |
|||
if (this.y < 0) { |
|||
this.y = 0; |
|||
} |
|||
if (this._autoKill) { |
|||
//note: iOS has a bug that throws off the scroll by several pixels, so we need to check if it's within 7 pixels of the previous one that we set instead of just looking for an exact match.
|
|||
if (!this.skipX && (xDif > threshold || xDif < -threshold) && x < _max(this._target, "x")) { |
|||
this.skipX = true; //if the user scrolls separately, we should stop tweening!
|
|||
} |
|||
if (!this.skipY && (yDif > threshold || yDif < -threshold) && y < _max(this._target, "y")) { |
|||
this.skipY = true; //if the user scrolls separately, we should stop tweening!
|
|||
} |
|||
if (this.skipX && this.skipY) { |
|||
this._tween.kill(); |
|||
if (this.vars.onAutoKill) { |
|||
this.vars.onAutoKill.apply(this.vars.onAutoKillScope || this._tween, this.vars.onAutoKillParams || []); |
|||
} |
|||
} |
|||
} |
|||
if (this._wdw) { |
|||
_window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y); |
|||
} else { |
|||
if (!this.skipY) { |
|||
this._target.scrollTop = this.y; |
|||
} |
|||
if (!this.skipX) { |
|||
this._target.scrollLeft = this.x; |
|||
} |
|||
} |
|||
this.xPrev = this.x; |
|||
this.yPrev = this.y; |
|||
} |
|||
|
|||
}), |
|||
p = ScrollToPlugin.prototype; |
|||
|
|||
ScrollToPlugin.max = _max; |
|||
ScrollToPlugin.getOffset = _getOffset; |
|||
ScrollToPlugin.buildGetter = _buildGetter; |
|||
ScrollToPlugin.autoKillThreshold = 7; |
|||
|
|||
p._kill = function(lookup) { |
|||
if (lookup.scrollTo_x) { |
|||
this.skipX = true; |
|||
} |
|||
if (lookup.scrollTo_y) { |
|||
this.skipY = true; |
|||
} |
|||
return this._super._kill.call(this, lookup); |
|||
}; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("ScrollToPlugin")); |
|||
@ -0,0 +1,76 @@ |
|||
/*! |
|||
* VERSION: 1.2.0 |
|||
* DATE: 2017-01-17 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* This file is to be used as a simple template for writing your own plugin. See the |
|||
* TweenPlugin docs for more details. |
|||
* |
|||
* You can start by doing a search for "yourCustomProperty" and replace it with whatever the name |
|||
* of your property is. This way of defining a plugin was introduced in version 1.9.0 - previous versions |
|||
* of TweenLite won't work with this. |
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
**/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
//ignore the line above this and at the very end - those are for ensuring things load in the proper order
|
|||
"use strict"; |
|||
|
|||
_gsScope._gsDefine.plugin({ |
|||
propName: "yourCustomProperty", //the name of the property that will get intercepted and handled by this plugin (obviously change it to whatever you want, typically it is camelCase starting with lowercase).
|
|||
priority: 0, //the priority in the rendering pipeline (0 by default). A priority of -1 would mean this plugin will run after all those with 0 or greater. A priority of 1 would get run before 0, etc. This only matters when a plugin relies on other plugins finishing their work before it runs (or visa-versa)
|
|||
API: 2, //the API should stay 2 - it just gives us a way to know the method/property structure so that if in the future we change to a different TweenPlugin architecture, we can identify this plugin's structure.
|
|||
version: "1.0.0", //your plugin's version number
|
|||
overwriteProps: ["yourCustomProperty"], //an array of property names whose tweens should be overwritten by this plugin. For example, if you create a "scale" plugin that handles both "scaleX" and "scaleY", the overwriteProps would be ["scaleX","scaleY"] so that if there's a scaleX or scaleY tween in-progress when a new "scale" tween starts (using this plugin), it would overwrite the scaleX or scaleY tween.
|
|||
|
|||
/* |
|||
* The init function is called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run. It receives 3 parameters: |
|||
* 1) target [object] - the target of the tween. In cases where the tween's original target is an array (or jQuery object), this target will be the individual object inside that array (a new plugin instance is created for each target in the array). For example, TweenLite.to([obj1, obj2, obj3], 1, {x:100}) the target will be obj1 or obj2 or obj3 rather than the array containing them. |
|||
* 2) value [*] - whatever value is passed as the special property value. For example, TweenLite.to(element, 1, {yourCustomProperty:3}) the value would be 3. Or for TweenLite.to(element, 1, {yourCustomProperty:{subProp1:3, subProp2:"whatever"}});, value would be {subProp1:3, subProp2:"whatever"}. |
|||
* 3) tween [TweenLite] - the TweenLite (or TweenMax) instance that is managing this plugin instance. This can be useful if you need to check certain state-related properties on the tween (maybe in the set method) like its duration or time. Most of the time, however, you don't need to do anything with the tween. It is provided just in case you want to reference it. |
|||
* 4) index [integer] - the index number of the target in the tween. For example, if an array is passed in as the target (or selector text), this would be 0 for the first one, 1 for the second, 2 for the third, etc. This was introduced in GSAP 1.19.0 |
|||
* |
|||
* This function should return true unless you want to have TweenLite/Max skip the plugin altogether and instead treat the property/value like a normal tween (as if the plugin wasn't activated). This is rarely useful, so you should almost always return true. |
|||
*/ |
|||
init: function(target, value, tween, index) { |
|||
this._target = target; //we record the target so that we can refer to it in the set method when doing updates.
|
|||
|
|||
/* Next, we create a property tween for "scaleX" and "scaleY" properties of our target |
|||
* (we're just using them as a examples of how to set up a property tween with a name, start, and end value). |
|||
* the _addTween() method accepts the following parameters: |
|||
* 1) target [object] - target object whose property this tween will control. |
|||
* 2) property [string] - the name of the property, like "scaleX" or "scaleY" |
|||
* 3) start [number] - The starting value of the property. For example, if you're tweening from 0 to 100, start would be 0. |
|||
* 4) end [number] - the ending value of the property. For example, if you're tweening from 0 to 100, end would be 100. |
|||
* 5) overwriteProperty [string] - the name that gets registered as the overwrite property so that if another concurrent tween of the same target gets created and it is tweening a property with this name, this one will be overwritten. Typically this is the same as "property". |
|||
* 6) round [boolean] - if true, the updated value on each update will be rounded to the nearest integer. [false by default] |
|||
* You do NOT need to use _addTween() at all. It is merely a convenience. You can record your own values internally or whatever you want. |
|||
*/ |
|||
this._addTween(target, "scaleX", target.scaleX, value, "scaleX", false); |
|||
this._addTween(target, "scaleY", target.scaleY, value, "scaleY", false); |
|||
|
|||
//now, just for kicks, we'll record the starting "alpha" value and amount of change so that we can manage this manually rather than _addTween() (again, totally fictitious, just for an example)
|
|||
this._alphaStart = target.alpha; |
|||
this._alphaChange = value.alpha - target.alpha; |
|||
|
|||
//always return true unless we want to scrap the plugin and have the value treated as a normal property tween (very uncommon)
|
|||
return true; |
|||
}, |
|||
|
|||
//[optional] - called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.). If you're using this._super._addTween() for all your tweens and you don't need to do anything special on each frame besides updating those values, you can omit this "set" function altogether.
|
|||
set: function(ratio) { |
|||
//since we used _addTween() inside init function, it created some property tweens that we'll update by calling the parent prototype's setRatio() (otherwise, the property tweens wouldn't get their values updated). this._super refers to the TweenPlugin prototype from which the plugin inherits (not that you need to worry about that).
|
|||
this._super.setRatio.call(this, ratio); |
|||
|
|||
//now manually set the alpha
|
|||
this._target.alpha = this._alphaStart + this._alphaChange * ratio; |
|||
} |
|||
|
|||
}); |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
@ -0,0 +1,163 @@ |
|||
/*! |
|||
* VERSION: 0.6.1 |
|||
* DATE: 2017-06-30 |
|||
* UPDATES AND DOCS AT: http://greensock.com
|
|||
* |
|||
* @license Copyright (c) 2008-2017, GreenSock. All rights reserved. |
|||
* This work is subject to the terms at http://greensock.com/standard-license or for
|
|||
* Club GreenSock members, the software agreement that was issued with your membership. |
|||
* |
|||
* @author: Jack Doyle, jack@greensock.com |
|||
*/ |
|||
var _gsScope = (typeof(module) !== "undefined" && module.exports && typeof(global) !== "undefined") ? global : this || window; //helps ensure compatibility with AMD/RequireJS and CommonJS/Node
|
|||
(_gsScope._gsQueue || (_gsScope._gsQueue = [])).push( function() { |
|||
|
|||
"use strict"; |
|||
|
|||
var _getText = function(e) { |
|||
var type = e.nodeType, |
|||
result = ""; |
|||
if (type === 1 || type === 9 || type === 11) { |
|||
if (typeof(e.textContent) === "string") { |
|||
return e.textContent; |
|||
} else { |
|||
for ( e = e.firstChild; e; e = e.nextSibling ) { |
|||
result += _getText(e); |
|||
} |
|||
} |
|||
} else if (type === 3 || type === 4) { |
|||
return e.nodeValue; |
|||
} |
|||
return result; |
|||
}, |
|||
_emojiStart = 0xD800, |
|||
_emojiEnd = 0xDBFF, |
|||
_emojiLowStart = 0xDC00, |
|||
_emojiRegionStart = 0x1F1E6, |
|||
_emojiRegionEnd = 0x1F1FF, |
|||
_emojiModStart = 0x1f3fb, |
|||
_emojiModEnd = 0x1f3ff, |
|||
_emojiPairCode = function(s) { |
|||
return ((s.charCodeAt(0) - _emojiStart) << 10) + (s.charCodeAt(1) - _emojiLowStart) + 0x10000; |
|||
}, |
|||
_emojiSafeSplit = function(text, delimiter) { //like calling String.split(delimiter) except that it keeps emoji characters together.
|
|||
if (delimiter !== "") { |
|||
return text.split(delimiter); |
|||
} |
|||
var l = text.length, |
|||
a = [], |
|||
character, i, emojiPair1, emojiPair2, j; |
|||
for (i = 0; i < l; i++) { |
|||
character = text.charAt(i); |
|||
if ((character.charCodeAt(0) >= _emojiStart && character.charCodeAt(0) <= _emojiEnd) || (text.charCodeAt(i+1) >= 0xFE00 && text.charCodeAt(i+1) <= 0xFE0F)) { //special emoji characters use 2 or 4 unicode characters that we must keep together.
|
|||
emojiPair1 = _emojiPairCode(text.substr(i, 2)); |
|||
emojiPair2 = _emojiPairCode(text.substr(i + 2, 2)); |
|||
j = ((emojiPair1 >= _emojiRegionStart && emojiPair1 <= _emojiRegionEnd && emojiPair2 >= _emojiRegionStart && emojiPair2 <= _emojiRegionEnd) || (emojiPair2 >= _emojiModStart && emojiPair2 <= _emojiModEnd)) ? 4 : 2; |
|||
a.push(text.substr(i, j)); |
|||
i += j - 1; |
|||
} else { |
|||
a.push(character); |
|||
} |
|||
} |
|||
return a; |
|||
}, |
|||
TextPlugin = _gsScope._gsDefine.plugin({ |
|||
propName: "text", |
|||
API: 2, |
|||
version:"0.6.1", |
|||
|
|||
//called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
|
|||
init: function(target, value, tween, index) { |
|||
var i = target.nodeName.toUpperCase(), |
|||
shrt; |
|||
if (typeof(value) === "function") { |
|||
value = value(index, target); |
|||
} |
|||
this._svg = (target.getBBox && (i === "TEXT" || i === "TSPAN")); |
|||
if (!("innerHTML" in target) && !this._svg) { |
|||
return false; |
|||
} |
|||
this._target = target; |
|||
if (typeof(value) !== "object") { |
|||
value = {value:value}; |
|||
} |
|||
if (value.value === undefined) { |
|||
this._text = this._original = [""]; |
|||
return true; |
|||
} |
|||
this._delimiter = value.delimiter || ""; |
|||
this._original = _emojiSafeSplit(_getText(target).replace(/\s+/g, " "), this._delimiter); |
|||
this._text = _emojiSafeSplit(value.value.replace(/\s+/g, " "), this._delimiter); |
|||
this._runBackwards = (tween.vars.runBackwards === true); |
|||
if (this._runBackwards) { |
|||
i = this._original; |
|||
this._original = this._text; |
|||
this._text = i; |
|||
} |
|||
if (typeof(value.newClass) === "string") { |
|||
this._newClass = value.newClass; |
|||
this._hasClass = true; |
|||
} |
|||
if (typeof(value.oldClass) === "string") { |
|||
this._oldClass = value.oldClass; |
|||
this._hasClass = true; |
|||
} |
|||
i = this._original.length - this._text.length; |
|||
shrt = (i < 0) ? this._original : this._text; |
|||
this._fillChar = value.fillChar || (value.padSpace ? " " : ""); |
|||
if (i < 0) { |
|||
i = -i; |
|||
} |
|||
while (--i > -1) { |
|||
shrt.push(this._fillChar); |
|||
} |
|||
return true; |
|||
}, |
|||
|
|||
//called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
|
|||
set: function(ratio) { |
|||
if (ratio > 1) { |
|||
ratio = 1; |
|||
} else if (ratio < 0) { |
|||
ratio = 0; |
|||
} |
|||
if (this._runBackwards) { |
|||
ratio = 1 - ratio; |
|||
} |
|||
var l = this._text.length, |
|||
i = (ratio * l + 0.5) | 0, |
|||
applyNew, applyOld, str; |
|||
if (this._hasClass) { |
|||
applyNew = (this._newClass && i !== 0); |
|||
applyOld = (this._oldClass && i !== l); |
|||
str = (applyNew ? "<span class='" + this._newClass + "'>" : "") + this._text.slice(0, i).join(this._delimiter) + (applyNew ? "</span>" : "") + (applyOld ? "<span class='" + this._oldClass + "'>" : "") + this._delimiter + this._original.slice(i).join(this._delimiter) + (applyOld ? "</span>" : ""); |
|||
} else { |
|||
str = this._text.slice(0, i).join(this._delimiter) + this._delimiter + this._original.slice(i).join(this._delimiter); |
|||
} |
|||
if (this._svg) { //SVG text elements don't have an "innerHTML" in Microsoft browsers.
|
|||
this._target.textContent = str; |
|||
} else { |
|||
this._target.innerHTML = (this._fillChar === " " && str.indexOf(" ") !== -1) ? str.split(" ").join(" ") : str; |
|||
} |
|||
} |
|||
|
|||
}), |
|||
p = TextPlugin.prototype; |
|||
|
|||
p._newClass = p._oldClass = p._delimiter = ""; |
|||
|
|||
}); if (_gsScope._gsDefine) { _gsScope._gsQueue.pop()(); } |
|||
|
|||
//export to AMD/RequireJS and CommonJS/Node (precursor to full modular build system coming at a later date)
|
|||
(function(name) { |
|||
"use strict"; |
|||
var getGlobal = function() { |
|||
return (_gsScope.GreenSockGlobals || _gsScope)[name]; |
|||
}; |
|||
if (typeof(module) !== "undefined" && module.exports) { //node
|
|||
require("../TweenLite.js"); |
|||
module.exports = getGlobal(); |
|||
} else if (typeof(define) === "function" && define.amd) { //AMD
|
|||
define(["TweenLite"], getGlobal); |
|||
} |
|||
}("TextPlugin")); |
|||
@ -0,0 +1,315 @@ |
|||
/* |
|||
@author: leeenx |
|||
@双向链表结构 |
|||
*/ |
|||
|
|||
export default class Chain { |
|||
constructor(arr = []) { |
|||
// 用于存储链表的数组
|
|||
this.chain = []; |
|||
this.chain.length = arr.length; |
|||
// 把 arr 转化为 chain
|
|||
for(let i = 0, len = arr.length; i < len; ++i) { |
|||
this.chain[i] = { |
|||
index: i, |
|||
prev: i - 1, |
|||
next: i !== len -1 ? i + 1 : -1, |
|||
data: arr[i] |
|||
} |
|||
} |
|||
|
|||
// 头指针
|
|||
this.HEAD = arr.length ? 0 : -1; |
|||
// 尾指针
|
|||
this.TAIL = arr.length ? arr.length - 1 : -1; |
|||
// 定位指针
|
|||
this.POINTER = -1; |
|||
// 自由指针
|
|||
this.FREE = arr.length; |
|||
// 自由列表 - 回收FREE
|
|||
this.FREELIST = []; |
|||
// 链表的长度
|
|||
this.length = this.chain.length; |
|||
// 创建一个迭代器
|
|||
this[Symbol.iterator] = () => { |
|||
// 指针指向头部
|
|||
let that = this, cur = that.chain[this.HEAD]; |
|||
return (function* () { |
|||
while(cur !== undefined) { |
|||
yield cur; |
|||
cur = that.chain[cur.next]; |
|||
} |
|||
}()); |
|||
} |
|||
} |
|||
// 返回链头并删除链头
|
|||
shift() { |
|||
// 回收 FREE
|
|||
this.collection(); |
|||
// FREE 指向被删除的位置
|
|||
this.FREE = this.HEAD; |
|||
// 头指头指向下一个位置
|
|||
this.HEAD = this.chain[this.HEAD].next; |
|||
// 当前头指针的 prev 指向 -1
|
|||
this.HEAD !== -1 && (this.chain[this.HEAD].prev = -1); |
|||
// 链表长度为0时,尾指针指向-1
|
|||
--this.length === 0 && (this.TAIL = -1); |
|||
// 返回 FREE
|
|||
return this.chain[this.FREE]; |
|||
} |
|||
// 插入新的链头
|
|||
unshift(item) { |
|||
// 新链表的第二个节点
|
|||
let second = this.HEAD; |
|||
// 头指针指向 FREE
|
|||
this.HEAD = this.FREE; |
|||
// 创建新的头节点
|
|||
this.chain[this.HEAD] = { |
|||
index: this.HEAD, |
|||
prev: -1, |
|||
next: second, |
|||
data: item |
|||
} |
|||
// 旧的头节点 prev 指向当前头节点
|
|||
if(second !== -1) { |
|||
this.chain[second].prev = this.HEAD; |
|||
} |
|||
else { |
|||
// 链表里只有一个节点,保证this.TAIL !== -1
|
|||
this.TAIL = this.HEAD; |
|||
} |
|||
// 创建一个 FREE
|
|||
this.calloc(); |
|||
// 链表长度 +1
|
|||
++this.length; |
|||
} |
|||
// 返回链尾并删除表尾
|
|||
pop() { |
|||
// 回收 FREE
|
|||
this.collection(); |
|||
// FREE 指向被删除的位置
|
|||
this.FREE = this.TAIL; |
|||
// 尾指针指向上一个位置
|
|||
this.TAIL = this.chain[this.TAIL].prev; |
|||
// 当前尾指针的 next 指向 -1
|
|||
this.TAIL !== -1 && (this.chain[this.TAIL].next = -1); |
|||
// 链表长度为0时,头指针指向-1
|
|||
--this.length === 0 && (this.HEAD = -1); |
|||
// 返回 FREE
|
|||
return this.chain[this.FREE]; |
|||
} |
|||
// 插入新的链尾
|
|||
push(item) { |
|||
// 新链表的倒数第二个节点
|
|||
let penultimate = this.TAIL; |
|||
// 尾指针指向 FREE
|
|||
this.TAIL = this.FREE; |
|||
// 创建新的尾节点
|
|||
this.chain[this.TAIL] = { |
|||
index: this.TAIL, |
|||
prev: penultimate, |
|||
next: -1, |
|||
data: item |
|||
} |
|||
|
|||
// 旧的尾节点 next 指向当前尾节点
|
|||
if(penultimate !== -1) { |
|||
this.chain[penultimate].next = this.TAIL; |
|||
} |
|||
else { |
|||
// 链表里只有一个节点,保证this.HEAD !== -1
|
|||
this.HEAD = this.TAIL; |
|||
} |
|||
// 创建一个 FREE
|
|||
this.calloc(); |
|||
// 链表长度 +1
|
|||
++this.length; |
|||
} |
|||
// 返回指定索引的元素
|
|||
at(index = 0) { |
|||
if(index < 0 || index >= this.length) return; |
|||
let cur; |
|||
if(index < this.length / 2) { |
|||
// 从头部开始
|
|||
cur = this.chain[this.HEAD]; |
|||
for(let i = 0; i !== index; ++i) { |
|||
cur = this.chain[cur.next]; |
|||
this.POINTER = cur.index; |
|||
} |
|||
} else { |
|||
// 从尾部开始
|
|||
cur = this.chain[this.TAIL]; |
|||
for(let i = this.length - 1; i !== index; --i) { |
|||
cur = this.chain[cur.prev]; |
|||
} |
|||
} |
|||
return cur; |
|||
} |
|||
// 返回指定指针的元素
|
|||
pointerAt(addr) { |
|||
// 指针地址索引在 0 ~ this.lenght,为合法地址
|
|||
if(addr >= 0 && addr < this.length) { |
|||
return this.chain[addr]; |
|||
} |
|||
} |
|||
// 返回第一个元素
|
|||
first() { |
|||
return this.chain[this.HEAD]; |
|||
} |
|||
// 返回最后一个元素
|
|||
last() { |
|||
return this.chain[this.TAIL]; |
|||
} |
|||
// 返回当前元素,并把 POINTER 指向上一个元素
|
|||
prev() { |
|||
// 当前节点
|
|||
let cur = this.curr(); |
|||
// POINTER 指向上一个,如果指向 -1 则重置为 this.HEAD
|
|||
this.POINTER !== -1 ? (this.POINTER = this.chain[this.POINTER].prev) : (this.POINTER = this.HEAD); |
|||
return cur; |
|||
} |
|||
// 返回前当元素,并把 POINTER 指向下一个元素
|
|||
next() { |
|||
// 当前节点
|
|||
let cur = this.curr(); |
|||
// POINTER 指向下一个,如果指向 -1 则重置为 this.TAIL
|
|||
this.POINTER !== -1 ? (this.POINTER = this.chain[this.POINTER].next) : (this.POINTER = this.TAIL); |
|||
return cur; |
|||
} |
|||
// 返回当前元素
|
|||
curr() { |
|||
return this.chain[this.POINTER]; |
|||
} |
|||
// 定位指针
|
|||
setPointer(addr = this.POINTER) { |
|||
// 指针地址索引在 0 ~ this.lenght,为合法地址
|
|||
if(addr >= 0 && addr < this.length) { |
|||
this.POINTER = addr; |
|||
return true; |
|||
} |
|||
} |
|||
// 克隆
|
|||
clone() { |
|||
let copy = new Chain(); |
|||
for(let key of ["HEAD", "TAIL", "FREE", "length"]) { |
|||
copy[key] = this[key]; |
|||
} |
|||
copy.chain.length = copy.length; |
|||
// 链表数据拷贝
|
|||
for(let i = 0, len = copy.length; i<len; ++i) { |
|||
copy.chain[i] = Object.create(this.chain[i]); |
|||
} |
|||
// FREELIST
|
|||
copy.FREELIST = [...this.FREELIST]; |
|||
return copy; |
|||
} |
|||
// 删除指定位置的元素
|
|||
remove(index) { |
|||
// 数组范围之外
|
|||
if(index<0 || index>=this.length) return ; |
|||
// 数组索引内
|
|||
if(index === 0) { |
|||
// 指向表头
|
|||
return this.shift(); |
|||
} else if(index === this.length - 1) { |
|||
// 指向表尾
|
|||
return this.pop(); |
|||
} else { |
|||
// 当前节点
|
|||
let cur = this.at(index); |
|||
// 上一个节点
|
|||
let prev = this.chain[cur.prev]; |
|||
// 下一个节点
|
|||
let next = this.chain[cur.next]; |
|||
// 回收 FREE
|
|||
this.collection(); |
|||
// FREE 指向被删除的位置
|
|||
this.FREE = cur.index; |
|||
// 删除当前节点
|
|||
[prev.next, next.prev] = [next.index, prev.index]; |
|||
// 链表长度 -1
|
|||
--this.length; |
|||
// 返回 FREE
|
|||
return cur; |
|||
} |
|||
} |
|||
// 在指定索引前插入元素
|
|||
insertBefore(index, item) { |
|||
// 数组范围之外
|
|||
if(index < 0 || index >= this.length) return ; |
|||
// 当前节点
|
|||
let cur = this.at(index); |
|||
// 上一个节点
|
|||
let prev = this.chain[cur.prev] || {index: -1}; |
|||
// 下一个节点
|
|||
let next = cur; |
|||
// 插入新节点
|
|||
cur = this.chain[this.FREE] = { |
|||
index: this.FREE, |
|||
prev: prev.index, |
|||
next: next.index, |
|||
data: item |
|||
} |
|||
next.prev = cur.index; |
|||
// 上一个节点存在
|
|||
if(prev.index !== -1) { |
|||
prev.next = cur.index; |
|||
} |
|||
// 插入的是头节点
|
|||
else { |
|||
this.HEAD = cur.index; |
|||
} |
|||
// 创建一个 FREE
|
|||
this.calloc(); |
|||
// 链表长度 +1
|
|||
++this.length; |
|||
} |
|||
// 在指定索引后插入元素,与 insertBefore 做对就
|
|||
insertAfter(index, item) { |
|||
// 数组范围之外
|
|||
if(index < 0 || index >= this.length) return ; |
|||
// 当前节点
|
|||
let cur = this.at(index); |
|||
// 下一个节点
|
|||
let next = this.chain[cur.next] || {index: -1}; |
|||
// 上一个节点
|
|||
let prev = cur; |
|||
// 插入新节点
|
|||
cur = this.chain[this.FREE] = { |
|||
index: this.FREE, |
|||
prev: prev.index, |
|||
next: next.index, |
|||
data: item |
|||
} |
|||
prev.next = cur.index; |
|||
// 下一个节点存在
|
|||
if(next.index !== -1) { |
|||
next.prev = cur.index; |
|||
} |
|||
// 插入的是尾节点
|
|||
else { |
|||
this.TAIL = cur.index; |
|||
} |
|||
// 创建一个 FREE
|
|||
this.calloc(); |
|||
// 链表长度 +1
|
|||
++this.length; |
|||
} |
|||
// 清空链表
|
|||
clean() { |
|||
// 清空数组
|
|||
this.length = this.chain.length = this.FREELIST.length = 0; |
|||
this.POINTER = this.HEAD = this.TAIL = -1; |
|||
this.FREE = 0; |
|||
} |
|||
// 动态分配 FREE
|
|||
calloc() { |
|||
// FREE 指向新位置
|
|||
this.FREE = this.FREELIST.length ? this.FREELIST.pop() : this.chain.length; |
|||
} |
|||
// 回收 FREE
|
|||
collection() { |
|||
// 把 FREE 加入到 FREELIST
|
|||
this.FREELIST.push(this.FREE); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
/* |
|||
@ author: leeenx |
|||
@ 事件封装 |
|||
@ object.on(event, fn) // 监听一个事件
|
|||
@ object.off(event, fn) // 取消监听
|
|||
@ object.once(event, fn) // 只监听一次事件
|
|||
@ object.dispacth(event, arg) // 触发一个事件
|
|||
*/ |
|||
|
|||
export default class Events { |
|||
constructor() { |
|||
// 定义的事件与回调
|
|||
this.defineEvent = {}; |
|||
} |
|||
// 注册事件
|
|||
register(event, cb) { |
|||
if(!this.defineEvent[event]) { |
|||
(this.defineEvent[event] = [cb]); |
|||
} |
|||
else { |
|||
this.defineEvent[event].push(cb); |
|||
} |
|||
} |
|||
// 派遣事件
|
|||
dispatch(event, arg) { |
|||
if(this.defineEvent[event]) {{ |
|||
for(let i=0, len = this.defineEvent[event].length; i<len; ++i) { |
|||
this.defineEvent[event][i] && this.defineEvent[event][i](arg); |
|||
} |
|||
}} |
|||
} |
|||
// on 监听
|
|||
on(event, cb) { |
|||
return this.register(event, cb); |
|||
} |
|||
// off 方法
|
|||
off(event, cb) { |
|||
if(this.defineEvent[event]) { |
|||
if(typeof(cb) == "undefined") { |
|||
delete this.defineEvent[event]; // 表示全部删除
|
|||
} else { |
|||
// 遍历查找
|
|||
for(let i=0, len=this.defineEvent[event].length; i<len; ++i) { |
|||
if(cb == this.defineEvent[event][i]) { |
|||
this.defineEvent[event][i] = null; // 标记为空 - 防止dispath 长度变化
|
|||
// 延时删除对应事件
|
|||
setTimeout(() => this.defineEvent[event].splice(i, 1), 0); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// once 方法,监听一次
|
|||
once(event, cb) { |
|||
let onceCb = () => { |
|||
cb && cb(); |
|||
this.off(event, onceCb); |
|||
} |
|||
this.register(event, onceCb); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/* |
|||
author: leeenx |
|||
date: 2017.09.08 |
|||
@ 获取不带描边的boudary |
|||
*/ |
|||
import * as PIXI from "../pixi"; |
|||
{ |
|||
let dirty = Symbol("dirty"); |
|||
let getContentBox = function () { |
|||
if (this[dirty] == this.dirty) return; |
|||
this[dirty] = this.dirty; // 表示已经更新
|
|||
let cp = this.clone(); |
|||
let graphicsData = cp.graphicsData; |
|||
for (let graphics of graphicsData) { |
|||
graphics.lineWidth = 0; |
|||
} |
|||
this._cwidth = cp.width; |
|||
this._cheight = cp.height; |
|||
}; |
|||
Object.defineProperties(PIXI.Graphics.prototype, { |
|||
_cwidth: { writable: true, value: 0 }, |
|||
_cheight: { writable: true, value: 0 }, |
|||
cwidth: { |
|||
get: function () { |
|||
getContentBox.call(this); |
|||
return this._cwidth; |
|||
}, |
|||
}, |
|||
cheight: { |
|||
get: function () { |
|||
getContentBox.call(this); |
|||
return this._cheight; |
|||
}, |
|||
}, |
|||
}); |
|||
} |
|||
@ -0,0 +1 @@ |
|||
PIXI.utils.skipHello(); |
|||
@ -0,0 +1,12 @@ |
|||
// es6 随机按顺序数组
|
|||
export default (list = [], count = list.length, filter) => |
|||
{ |
|||
list = [].concat(list); // 切断与参数list 的耦合
|
|||
list = typeof filter === "function" ? list.filter( (...args) => filter(...args) ) : list; |
|||
let res = []; |
|||
for(let i=0; i<count; ++i) { |
|||
let [len, randomIndex] = [list.length, (Math.random() * list.length)>>0]; |
|||
res = res.concat(list.splice(randomIndex, 1)); |
|||
} |
|||
return res.length == 1 ? res[0] : res; |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
/* |
|||
author: leeenx |
|||
@ ticker 对象 |
|||
@ 提供 5 个API如下: |
|||
@ ticker.addEventListener |
|||
@ ticker.removeEventListener |
|||
@ ticker.pause - 暂停 |
|||
@ ticker.resume - 恢复 |
|||
@ ticker.paused - 暂停状态 |
|||
@ 这里直接借用 GSAP.TweenMax.ticker |
|||
*/ |
|||
import TweenMax from "../gsap/TweenMax"; |
|||
let ticker = {}; |
|||
ticker.paused = 0; |
|||
(ticker.pause = () => (ticker.paused = 1)), TweenMax.pauseAll(); |
|||
(ticker.resume = () => (ticker.paused = 0)), TweenMax.resumeAll(); |
|||
ticker.addEventListener = (...args) => |
|||
TweenMax.ticker.addEventListener(...args); |
|||
ticker.removeEventListener = (...args) => |
|||
TweenMax.ticker.removeEventListener(...args); |
|||
|
|||
TweenMax.ticker.addEventListener("tick", () => { |
|||
ticker.elapsedMS = TweenMax.ticker.time - TweenMax.ticker._time; |
|||
TweenMax.ticker._time = TweenMax.ticker.time; |
|||
}); |
|||
|
|||
export default ticker; |
|||
@ -0,0 +1,176 @@ |
|||
/* |
|||
author: leeenx |
|||
@ timer 对象 |
|||
@ 提供 7 个API如下: |
|||
@ timer.setTimeout(fun, delay[, id]) |
|||
@ timer.clearTimeout(id) |
|||
@ timer.setInterval(fun, delay[, id]) |
|||
@ timer.clearInterval(id) |
|||
@ timer.delete(id) 删除对应id的timeout/interval |
|||
@ timer.pause(id) 暂停对应id的timeout/interval |
|||
@ timer.resume(id) 恢复对应id的timeout/interval |
|||
@ timer.clean() 清空所有 timeout & interval |
|||
*/ |
|||
|
|||
class Timer { |
|||
// 构造函数
|
|||
constructor() { |
|||
// 暂停状态 - 这是个公共状态,由外部 Ticker 决定。
|
|||
this.paused = true; |
|||
|
|||
// 队列
|
|||
this.queue = new Map(); |
|||
|
|||
// 正在使用 timer 的 RAF
|
|||
this.usingRAF = false; |
|||
|
|||
// useRAF 触发器
|
|||
Reflect.defineProperty(this, "useRAF", { |
|||
set: function(value) { |
|||
Reflect.set(this, "usingRAF", value); |
|||
value ? Timer.RAF.enable() : Timer.RAF.disable(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// setTimeout 的实现
|
|||
setTimeout(fn, delay, id = Symbol("timeoutID")) { |
|||
// 存入队列
|
|||
this.queue.set(id, { |
|||
fn: fn, |
|||
type: 0, |
|||
paused: 0, |
|||
elapsed: 0, |
|||
delay: delay |
|||
}); |
|||
return id; |
|||
} |
|||
|
|||
// clearTimeout
|
|||
clearTimeout(id) { |
|||
this.delete(id); |
|||
} |
|||
|
|||
// setInterval 的实现
|
|||
setInterval(fn, delay, id = Symbol("intervalID")) { |
|||
// 存入队列
|
|||
this.queue.set(id, { |
|||
fn: fn, |
|||
type: 1, |
|||
paused: 0, |
|||
elapsed: 0, |
|||
delay: delay |
|||
}); |
|||
return id; |
|||
} |
|||
|
|||
// clearInterval
|
|||
clearInterval(id) { |
|||
this.delete(id); |
|||
} |
|||
|
|||
// 修改指定id的 delay/fn
|
|||
set(id, config = {}) { |
|||
let item = this.queue.get(id) || {}; |
|||
for(let key in config) { |
|||
item[key] = config[key]; |
|||
} |
|||
} |
|||
|
|||
// 删除 queue 上的成员
|
|||
delete(id) { |
|||
this.queue.delete(id); |
|||
} |
|||
|
|||
// 暂停指定id
|
|||
pause(id) { |
|||
id === undefined ? this.pauseAll() : (this.queue.get(id).paused = 1); |
|||
} |
|||
|
|||
// 恢复指定id
|
|||
resume(id) { |
|||
this.play(id); |
|||
} |
|||
|
|||
// 播放指定id
|
|||
play(id) { |
|||
id === undefined ? this.playAll() : (this.queue.get(id).paused = 0); |
|||
} |
|||
|
|||
// 清空timer
|
|||
clean() { |
|||
this.queue = new Map(); |
|||
} |
|||
|
|||
// 暂停全部 id
|
|||
pauseAll() { |
|||
this.queue.forEach((item) => item.paused = 1); |
|||
} |
|||
|
|||
// 播放全部 id
|
|||
playAll() { |
|||
this.queue.forEach((item) => item.paused = 0); |
|||
} |
|||
|
|||
// tick
|
|||
tick(delta) { |
|||
this.paused || this.updateQueue(delta); |
|||
} |
|||
|
|||
// 更新 map 队列
|
|||
updateQueue(delta) { |
|||
this.queue.forEach((item, id) => { |
|||
if(item.paused === 1) return; |
|||
item.elapsed += delta; |
|||
if(item.elapsed >= item.delay) { |
|||
item.fn(); |
|||
item.type === 0 ? this.delete(id) : (item.elapsed = 0); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
// 状态更新
|
|||
update() { |
|||
// 第一次调用 update 时主动停用原生接口
|
|||
this.useRAF = false; |
|||
|
|||
// 下面是真正的 update
|
|||
this.update = (paused, delta) => { |
|||
if(this.usingRAF) return; |
|||
// 播放开关
|
|||
this.paused == paused || (this.paused = paused); |
|||
this.tick(delta); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
class AnimationFrame { |
|||
constructor() { |
|||
this.time = 0; |
|||
this.auto = this.auto.bind(this); |
|||
} |
|||
auto(elapsed) { |
|||
timer.tick(elapsed - this.time); |
|||
this.time = elapsed; |
|||
this.id = requestAnimationFrame(this.auto); |
|||
} |
|||
enable() { |
|||
timer.paused = false; |
|||
this.id = requestAnimationFrame(this.auto); |
|||
} |
|||
disable() { |
|||
cancelAnimationFrame(this.id); |
|||
} |
|||
} |
|||
|
|||
// 原生RAF
|
|||
Timer.RAF = new AnimationFrame(); |
|||
|
|||
// 对外接口
|
|||
let timer = new Timer(); |
|||
|
|||
// 默认使用原生 RAF
|
|||
timer.useRAF = true; |
|||
// 导出timer
|
|||
export default timer; |
|||
@ -0,0 +1,7 @@ |
|||
.snake { |
|||
position: relative; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-image: url(./bg.jpg); |
|||
background-size: cover; |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
import "./snake.css"; |
|||
import SnakeModel from "./core/model"; |
|||
|
|||
// view
|
|||
import SnakeView from "./core/view"; |
|||
|
|||
// control
|
|||
import SnakeControl from "./core/control"; |
|||
|
|||
export default class SnakeClass extends SnakeControl { |
|||
constructor({ containerId, onLose }) { |
|||
const container = document.getElementById(containerId); |
|||
const width = container.clientWidth; |
|||
const row = 14; |
|||
const column = 18; |
|||
const height = (width / column) * row; |
|||
const bg = document.createElement("div"); |
|||
bg.className = "snake"; |
|||
bg.style.paddingTop = `${(240 / 2160) * width}px`; |
|||
container.appendChild(bg); |
|||
|
|||
super(new SnakeModel(), new SnakeView(bg, width, height)); |
|||
|
|||
this.init({ |
|||
time: 3000000, // 总时间
|
|||
width, |
|||
height, |
|||
row, |
|||
column, |
|||
// 显示屏的边框
|
|||
border: 0x414042, |
|||
color: 0x414042, // 蛇的节点颜色
|
|||
food: 0x990000, // 食物的颜色
|
|||
min: 4, // 初始长度
|
|||
speed: 1.5, // 速度标量
|
|||
}); |
|||
this.event.on("gameover", onLose); |
|||
// snakeGame.event.on("eat", (food) => {
|
|||
// console.log("吃到食物,当前长度: " + snakeGame.length);
|
|||
// });
|
|||
this.start(); |
|||
} |
|||
dispose() {} |
|||
addControl() { |
|||
let controller = document.querySelector(".snake-direction"), |
|||
curDirection, |
|||
{ top, left, width, height } = controller.getBoundingClientRect(), |
|||
x = left + width / 2, |
|||
y = top + height / 2, |
|||
deg45 = Math.PI / 4, |
|||
deg90 = Math.PI / 2, |
|||
deg135 = Math.PI / 2 + deg45, |
|||
deg180 = Math.PI, |
|||
deg225 = Math.PI + deg45, |
|||
deg270 = Math.PI + deg90, |
|||
deg315 = Math.PI * 2 - deg45; |
|||
|
|||
controller.addEventListener( |
|||
"touchstart", |
|||
({ targetTouches: [{ pageX, pageY }] }) => { |
|||
checkDirection(pageX - x, pageY - y); |
|||
} |
|||
); |
|||
|
|||
controller.addEventListener( |
|||
"touchmove", |
|||
({ targetTouches: [{ pageX, pageY }] }) => { |
|||
checkDirection(pageX - x, pageY - y); |
|||
} |
|||
); |
|||
|
|||
controller.addEventListener( |
|||
"touchend", |
|||
({ changedTouches: [{ pageX, pageY }] }) => { |
|||
curDirection = undefined; |
|||
controller.className = "snake-direction"; |
|||
} |
|||
); |
|||
|
|||
let checkDirection = function (x, y) { |
|||
let radian = Math.asin(y / Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))); |
|||
// 1~2象限
|
|||
if ((x > 0 && y < 0) || (x > 0 && y > 0)) { |
|||
radian += deg90; |
|||
} |
|||
// 3~4象限
|
|||
else if ((x < 0 && y > 0) || (x < 0 && y < 0)) { |
|||
radian = deg270 - radian; |
|||
} |
|||
|
|||
let direction = "up"; |
|||
if (radian > deg45 && radian < deg135) { |
|||
direction = "right"; |
|||
} else if (radian > deg135 && radian < deg225) { |
|||
direction = "down"; |
|||
} else if (radian > deg225 && radian < deg315) { |
|||
direction = "left"; |
|||
} |
|||
direction === curDirection || |
|||
snakeGame.turn( |
|||
(curDirection = direction), |
|||
(controller.className = "snake-direction " + direction) |
|||
); |
|||
}; |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 104 KiB |