Created
October 31, 2017 08:09
-
-
Save yibuyisheng/b07fced50502295d813b7239c4f9d962 to your computer and use it in GitHub Desktop.
Animate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file 逐帧动效类 | |
* @author yibuyisheng | |
*/ | |
/** | |
* 逐帧动效类 | |
* | |
* @class | |
*/ | |
export default class { | |
/** | |
* 动效依附的DOM元素,使用background实现动效 | |
* | |
* @type {HTMLElement} | |
* @private | |
*/ | |
element = null; | |
/** | |
* 每张小图的宽度(每张小图的宽度必须是一样的) | |
* | |
* @type {number} | |
* @private | |
*/ | |
width = 0; | |
/** | |
* 每张小图的高度(每张小图的高度必须是一样的) | |
* | |
* @type {number} | |
* @private | |
*/ | |
height = 0; | |
/** | |
* 整个大图每行有多少个小图 | |
* | |
* @type {number} | |
* @private | |
*/ | |
countPerLine = 0; | |
/** | |
* 其它一些参数,此处支持入场(show)、鼠标移入(hover)、鼠标移出(out)三种动画效果,这三种效果可以通过options配置, | |
* 每种动效的start代表起始图片是第几张,end代表结束图片是第几张。 | |
* | |
* @type Object | |
* @private | |
*/ | |
options = { | |
ranges: { | |
show: { | |
start: 0, | |
end: 0 | |
}, | |
hover: { | |
start: 0, | |
end: 0 | |
}, | |
out: { | |
start: 0, | |
end: 0 | |
} | |
} | |
}; | |
/** | |
* 动效任务列表,主要是为了防止后续的动效覆盖掉前面的动效,出现“突变”效果。 | |
* 每一个task函数都有一个动画执行完成的回调函数。 | |
* | |
* 每个元素的结构如下: | |
* { | |
* fn: function (callback) { ... }, | |
* name: 'show' // 入场动画,目前支持的取值为:show、hover、out | |
* } | |
* | |
* @type {Array.<Object>} | |
* @private | |
*/ | |
animationTasks = []; | |
/** | |
* 当前动效到哪张小图了 | |
* | |
* @type {number} | |
* @private | |
*/ | |
current = 0; | |
/** | |
* constructor | |
* | |
* @public | |
* @param {Element} element 动画元素 | |
* @param {number} width 每张小图的宽度 | |
* @param {number} height 每张小图的高度 | |
* @param {number} countPerLine 每一行小图的数量 | |
* @param {Object} options 额外参数 | |
*/ | |
constructor(element, width, height, countPerLine, options) { | |
this.element = element; | |
this.width = width; | |
this.height = height; | |
this.countPerLine = countPerLine || 17; | |
this.options = options || this.options; | |
} | |
/** | |
* 执行动画的基本方法 | |
* | |
* @private | |
* @param {number} start 起始图片(在大图中第几张) | |
* @param {number} end 结束图片(在大图中第几张) | |
* @param {number} speed 动画执行速度 | |
* @param {Function} completeCallback 完成动画的回调函数 | |
*/ | |
animate(start, end, speed = 1, completeCallback) { | |
this.isPaused = false; | |
let me = this; | |
let speedCounter = 0; | |
this.current = start; | |
run(start); | |
function run() { | |
requestAnimationFrame(function () { | |
if (speedCounter++ < speed) { | |
return run(me.current); | |
} | |
speedCounter = 0; | |
let x = -((me.current - 1) % me.countPerLine) * me.width; | |
let y = -Math.floor((me.current - 1) / me.countPerLine) * me.height; | |
me.element.style.backgroundPosition = x + 'px ' + y + 'px'; | |
if (!me.isPaused && me.current < end) { | |
++me.current; | |
run(); | |
} | |
else { | |
completeCallback && completeCallback(); | |
} | |
}); | |
} | |
} | |
/** | |
* 暂停 | |
* | |
* @public | |
*/ | |
pause() { | |
this.isPaused = true; | |
} | |
/** | |
* 添加动效任务。如果队列当中已经存在相应name的动画,就不再添加了。 | |
* | |
* @private | |
* @param {Function} taskFn 动效函数,带有一个动效完成回调函数参数 | |
* @param {string} name 动画名 | |
*/ | |
addTask(taskFn, name) { | |
if (!this.animationTasks.some(task => task.name === name)) { | |
this.animationTasks.push({ | |
fn: taskFn, | |
name: name | |
}); | |
} | |
} | |
/** | |
* 开始循环检测动效任务列表,只要发现有动效任务,就执行。 | |
* 一个实例只能调用一次。 | |
* | |
* @public | |
*/ | |
startExecute() { | |
let tasks = this.animationTasks; | |
run(); | |
function run() { | |
requestAnimationFrame(function () { | |
if (tasks.length) { | |
execute(run); | |
} | |
else { | |
run(); | |
} | |
}); | |
} | |
function execute(completeCallback) { | |
let task = tasks[0]; | |
if (task) { | |
task.fn(() => { | |
// 动画执行完成,清理一下队列,同时开始下一个动画的执行 | |
tasks.shift(); | |
execute(completeCallback); | |
}); | |
} | |
else { | |
completeCallback && completeCallback(); | |
} | |
} | |
} | |
/** | |
* 入场动效。 | |
* | |
* @public | |
*/ | |
show() { | |
let rangesShow = this.options.ranges.show; | |
this.addTask( | |
completeCallback => this.animate(rangesShow.start, rangesShow.end, 3, completeCallback), | |
'show' | |
); | |
} | |
/** | |
* 鼠标移入动效 | |
* | |
* @public | |
*/ | |
hover() { | |
let rangesHover = this.options.ranges.hover; | |
this.addTask( | |
completeCallback => this.animate(rangesHover.start, rangesHover.end, 2, completeCallback), | |
'hover' | |
); | |
} | |
/** | |
* 鼠标移出动效 | |
* | |
* @public | |
*/ | |
out() { | |
let me = this; | |
let rangesOut = me.options.ranges.out; | |
this.addTask( | |
completeCallback => this.animate(rangesOut.start, rangesOut.end, 1, completeCallback), | |
'out' | |
); | |
} | |
/** | |
* 绑定鼠标事件 | |
* | |
* @public | |
*/ | |
bindMouseEvents() { | |
this.element.addEventListener('mouseenter', mouseenter); | |
this.element.addEventListener('mouseout', mouseout); | |
let me = this; | |
function mouseenter() { | |
me.hover(); | |
} | |
function mouseout() { | |
me.out(); | |
} | |
} | |
/** | |
* 绑定“入场”事件,在入场的时候执行相应动画 | |
* | |
* @public | |
*/ | |
bindShow() { | |
let me = this; | |
document.addEventListener('scroll', scroll); | |
// 初始化检查一下 | |
scroll(); | |
function scroll() { | |
let rect = me.element.getBoundingClientRect(); | |
let viewHeight = window.innerHeight; | |
let buffer = 50; | |
if (rect.top > viewHeight - buffer || rect.bottom < buffer) { | |
return; | |
} | |
me.show(); | |
document.removeEventListener('scroll', scroll); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment