impress.js 源码分析

  之前做展示用幻灯片,我一直热衷于使用PPT,刚开始学习PPT时总是强行使用各种页面特效,越做越复杂。现在看来,学技术大概都要经历一个从简到繁再到简的过程吧。后来,无意间接触到prezi,被它强大的展示逻辑所折服,但用了段时间,发现使用prezi破解版有诸多不便,最关键的是,除了很炫的转场特效,单页的设计感不及PPT,总感觉不尽如人意。
  总结下,PPT的单页设计感强,普及率高;prezi的展示思路清晰,变现力强,跨平台,但制作流程稍显不便。
  最近,学习H5+CSS3时我突发奇想,为何不用H5来做幻灯片展示呢?刚开始两天,纯靠自己手写页面和转场,不是一般累。我试图去抽取常用的公共方法,尝试做成框架组件的形式,发现工作量巨大,而且以自己目前水平,写出来的代码通用性很差。在Google上搜索时,我发现了impress.js的存在,与我的设想不谋而合,于是乎……不再自己造轮子,又花了两天时间熟悉使用impress.js来设计幻灯片,效果完全超越了我的预期。
  impress.js简单来说仅仅是实现了幻灯片的转场特效的框架,虽说将单页限制在框架之内,但所有单页还是需要自己用代码设计。虽然花费的时间远远超过了PPT和prezi,但是能够完全使用代码来定制幻灯片,能够使用几乎每台电脑必备的浏览器放幻灯片,何尝不是一种幸福!
  花了一天探索了下impress.js源码,其实并不复杂,个人感觉收获颇丰,以下阐述我的收获。

1. impress.js华丽效果实现方法概述

  通过审查元素发现,页面间转场的实现全部是依赖于CSS3,准确说是translate3d属性完成的。impress的水平移动是改变了translateX坐标,垂直移动是改变translateY坐标,而变小变大的绚丽效果是改变translateZ的坐标实现,而这些转化样式的事件监听是通过js来实现的。

2. impress.js具体的技术实现

2.1 动效参数的获取

(line307):参数获取
官方提供了一个demo:index.html,首先从data-* 属性入手。这是html5的新增api,允许用户自定义数据,通过dataset方法取出数据

var data = el.dataset, //el是通过getElememtById()获得的元素
step = {   //定义了一个step对象,里面有4个css样式属性translate: {x: toNumber(data.x),y: toNumber(data.y),z: toNumber(data.z)},  //toNumber函数将参数转换成数字,如果无法转换返回默认值rotate: {x: toNumber(data.rotateX),y: toNumber(data.rotateY),z: toNumber(data.rotateZ || data.rotate)},scale: toNumber(data.scale, 1),el: el
};

在浏览器的console调试代码,元素的dataset 得到的是一个数组,可以依次取出x,y,z值,这就是动效实现的核心。

2.2 通用函数定义

(line1-line174):通用函数定义

pfx() ----- 通过检测浏览器给css3属性加上当前浏览器可用的前缀
arrayify() ----- 将Array-Like对象转换成Array对象
css() ----- 将指定属性应用到指定元素上
toNumber() ----- 将参数转换成数字,如果无法转换返回默认值
byId() ----- 通过id获取元素
$() ----- 返回满足选择器的第一个元素
$$() ----- 返回满足选择器的所有元素
triggerEvent() ----- 在指定元素上触发指定事件
translate() ----- 将translate对象转换成css使用的字符串
rotate() ----- 将rotate对象转换成css使用的字符串
scale() ----- 将scale对象转换成css使用的字符串
perspective() ----- 将perspective对象转换成css使用的字符串
getElementFromHash() ----- 根据hash来获取元素(hash是URL中形如#step1的东西)
computeWindowScale() ----- 根据当前窗口尺寸计算scale,用于放大和缩小

2.3 主函数与5大api

(223line~):主函数与5大api
API: goto(), init(), next(), prev(), initStep()
主函数: var impress = window.impress = function ( rootId ) {……}
在index.html中调用了init()函数

<script src="js/impress.js"></script>
<script>
impress().init();
</script>

源码中的init()函数,分析写在注释中

var init = function() {if (initialized) {return; } //初始值initialized=false;//使用viewport支持移动设备var meta = $("meta[name='viewport']") || document.createElement("meta");// 定义的$风格函数,类似于jQuery//  var $ = function ( selector, context ) {//      context = context || document;//      return context.querySelector(selector);//  };meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";if (meta.parentNode !== document.head) { //判断meta的parentNode节点是不是<head>meta.name = 'viewport'; //如果不是head标签,就js添加一个meta标签document.head.appendChild(meta);}//初始化配置root (243line & 269line)var rootData = root.dataset; //获取到初始化的root数据,即id=“impress”的div标签里的内容config = {width: toNumber(rootData.width, defaults.width),height: toNumber(rootData.height, defaults.height),maxScale: toNumber(rootData.maxScale, defaults.maxScale),minScale: toNumber(rootData.minScale, defaults.minScale),perspective: toNumber(rootData.perspective, defaults.perspective),transitionDuration: toNumber(rootData.transitionDuration, defaults.transitionDuration)};windowScale = computeWindowScale(config);// wrap steps with "canvas" elementarrayify(root.childNodes).forEach(function(el) {canvas.appendChild(el);});root.appendChild(canvas);// arraify函数把类数组对象转化为真正数组(69line)document.documentElement.style.height = "100%";css(body, {height: "100%",overflow: "hidden"});var rootStyles = {position: "absolute",transformOrigin: "top left",transition: "all 0s ease-in-out",transformStyle: "preserve-3d"};css(root, rootStyles);css(root, {top: "50%",left: "50%",transform: perspective(config.perspective / windowScale) + scale(windowScale)});css(canvas, rootStyles);body.classList.remove("impress-disabled");body.classList.add("impress-enabled");// get and init stepssteps = $$(".step", root);steps.forEach(initStep);// 找到每一个class为”step“的元素,返回root(id=“impress”)的数组// forEach遍历每一个数组,对每个div用initstep()函数初始化//即我们一开始分析的那个函数。主要是把data-*自定义的数据获得,附上transtion样式。// set a default initial state of the canvascurrentState = {translate: { x: 0, y: 0, z: 0 },rotate: { x: 0, y: 0, z: 0 },scale: 1};//当前的状态。位移为0,旋转为0,缩放为1.   initialized = true;//初始化为true,即完成初始化//triggerEvent()自定义事件监听函数//其中,第一个参数为要处理的事件名//第二个参数为表明事件是否冒泡//第三个参数为表明是否可以取消事件的默认行为//第四个参数为细节参数triggerEvent(root, "impress:init", { api: roots["impress-root-" + rootId] });
};

总结:初始化过程分为两个阶段,第一个阶段是运行init()函数,第二个阶段是运行绑定到impress:init上的函数。在init()函数的结尾触发impress:init事件,这样绑定上去的函数就会全部触发。

2.4 事件对象绑定与监听

root.addEventListener("impress:init", function() {// STEP CLASSESsteps.forEach(function(step) {step.classList.add("future");});root.addEventListener("impress:stepenter", function(event) {event.target.classList.remove("past");//利用html5 classList属性对class类增删改查event.target.classList.remove("future");event.target.classList.add("present");}, false);root.addEventListener("impress:stepleave", function(event) {event.target.classList.remove("present");event.target.classList.add("past");}, false);}, false);

init是初始化事件,stepenter是进入下一步事件,stepleave是离开上一步事件

var onStepEnter = function(step) {if (lastEntered !== step) {triggerEvent(step, "impress:stepenter");lastEntered = step;}
};
var onStepLeave = function(step) {if (lastEntered === step) {triggerEvent(step, "impress:stepleave");lastEntered = null;}
};

一个step就是一个页面,按一次键盘上的left键或者right键就会切换一次step,同时绑定键盘事件

document.addEventListener("keyup", function ( event ) {...}
document.addEventListener("keydown", function ( event ) {...}
document.addEventListener("click", function ( event ) {...}
window.addEventListener("resize", throttle(function () {...}
document.addEventListener("touchstart", function ( event ) {...}

函数简单总结

impress 主函数,构造impress对象,为全局对象
onStepEnter 用于触发impress:stepenter事件
onStepLeave 用于触发impress:stepleave事件
initStep 初始化给定 step
init 主初始化函数
getStep 获取指定 step
goto 切换到指定step
prev 切换到上一个 step
next 切换到下一个step

3 总结

  总体看来,impress.js源码简洁明了,并不复杂,作者的本意也是构建一个基础的框架,让使用者自由发挥,正合吾意!很多技术单独实现都很简单,综合运用起来,如何保证命名空间不污染,代码的重用复用,js和css代码的兼容性等细节都是值得学习的地方。

impress.js 源码分析相关推荐

  1. video.js 源码分析(JavaScript)

    video.js 源码分析(JavaScript) 组织结构 继承关系 运行机制 插件的运行机制 插件的定义 插件的运行 控制条是如何运行的 UI与JavaScript对象的衔接 类的挂载方式 存储 ...

  2. Vue.js 源码分析(二十三) 指令篇 v-show指令详解

    v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...

  3. Vue.js 源码分析(九) 基础篇 生命周期详解

    先来看看官网的介绍: 主要有八个生命周期,分别是: beforeCreate.created.beforeMount.mounted.beforeupdate.updated   .beforeDes ...

  4. Vue.js 源码分析(五) 基础篇 方法 methods属性详解

    methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...

  5. 补环境:vm2 transformer.js 源码分析

    在补环境框架的文件夹里执行 vm2 文件能成功得到结果,但是将合并了环境和原 js 文件后的代码内容单独提取出来通过 vm2 调用却报错提示 SyntaxError: Use of internal ...

  6. 移动端开发基本知识之touch.js,FastClick.js源码分析

    问题1:300ms延迟问题指的是? 不管在移动端还是PC端,我们都需要处理用户点击,这个最常用的事件.但在touch端click事件响应速度会比较慢,在较老的手机设备上会更为明显(300ms的延迟). ...

  7. js源码分析(一)——Call

    js源码(call): <!DOCTYPE html> <html lang="en"> <head><meta charset=&quo ...

  8. underscore.js 源码分析5 基础函数和each函数的使用

    isArrayLike 检测是数组对象还是纯数组 var property = function(key) {return function(obj) {return obj == null ? vo ...

  9. Vue.js 源码分析—— Slots 是如何实现的

    今天主要分析 Vue.js 中常用的 Slots 功能是如何设计和实现的.本文将分为普通插槽.作用域插槽以及 Vue.js 2.6.x 版本的 v-slot 语法三部分进行讨论. 本文属于进阶内容,如 ...

最新文章

  1. PostgreSQL在何处处理 sql查询之五十二
  2. 《火球——UML大战需求分析》(第2章 耗尽脑汁的需求分析工作)——2.3 给客户带来价值,需求分析之正路...
  3. opencv进阶学习笔记7:直方图,直方图均衡化,直方图比较,直方图反向投影
  4. leetcode--200. 岛屿的个数
  5. Angular DefaultDomRenderer2.setProperty - HTML的值是如何从Angular Component flow过来的,以及跨平台支持
  6. 图神经网络(一)图信号处理与图卷积神经网络(5)图卷积神经网络
  7. 数据结构与算法--解决问题的方法- 二叉树的的镜像
  8. 悲观锁和乐观锁_带你了解MySQL中的乐观锁与悲观锁
  9. 聊聊人工智能领域的工作状态?知乎回答
  10. python(45)内置函数:os.system() 和 os.popen()
  11. 容智RPA可以在医疗哪些业务上降本增效
  12. matlab无法用mcc,使用matlab的mcc时为什么会出现fopen错误?
  13. cipher加密解密
  14. Springboot整合JdbcTemplate实现分页查询
  15. 数据分析:帕累托法则分析菜品盈利
  16. 三体归零者和盘龙鸿蒙,《三体》里归零者那么厉害,为什么不能逆转降维打击?...
  17. 智慧校园管理系统,精细化+网格化
  18. storm和vgj vgj_Team VGJ发布公告解散 两年征程划上句号
  19. 智能优化算法(源码)-沙丘猫群优化(SCSO)
  20. Docker Swarm从部署到基本操作

热门文章

  1. Word中打不出来的符号公式
  2. 50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
  3. android 横向listview左右滑动,实现可以横向滑动的Listview
  4. POI操作Word组件haiwei-poi-word(模板+数据,简单几行代码就可以实现对word的输出)
  5. CKEditor 4下载和使用
  6. 用sql语句快速生成大量数据,批量生成数据
  7. IDEA在java文件中按住ctrl进入java类文件的其他对象中为什么新打开的窗口被覆盖了?而不是新打开一个窗口?怎么设置回来?
  8. 牛客网牛牛换瓷砖的编程题
  9. Android高级-贝塞尔曲线与计算规则
  10. nginx详细介绍使用