简介
随着H5应用的兴起和发展,交互式一镜到底逐渐成为广告新宠。这种不用翻页、无需硬切换场景的营销手段,可以根据时间的推进或故事的开展为设计灵感,将某个过程、某段经历、某个场景循序渐进地铺陈开来。
一镜到底式H5的具体玩法是多种多样的,99%都有套路可循,主要有以下几类:
一、视频类
以第一视角展示完整故事或宏大场景,先把视频做好,再将其融入到H5作品中。
二、长页面类
以X轴或Y轴运动突出整体的连续性,我们还可在上下左右滑动的基础上,将视差动画等创意形式融入其中,以便丰富画面的层次感并让作品更加立体。
三、立体穿梭类
以Z轴运动或画中画实现空间穿梭或场景穿梭
实践
今年年终h5三娃回家,使用了长页面的形式,随着右滑的操作,场景逐帧呈现,动画也随之出现,用户与三娃共同体验春节回家的两种历程。
技术上采用了Pixi.js + Tween.js + Animate.js
体验二维码
绘制场景内容——Pixi.js
创建舞台(stage)和渲染器 (renderer)
舞台:所有要渲染的对象都必须连接到舞台中才能被显示出来,舞台处于整个树形展示结构的最底层,可以理解为背景。
渲染器:选用canvas或webGL进行渲染的一个区域
//创建一个容器对象:舞台
var objPixiContainer = new PIXI.Container();
//创建渲染器
var pixiRender = new PIXI.CanvasRenderer(windowWidth, windowHeight);
//告诉渲染器去渲染舞台
pixiRender.render(objPixiContainer);
创建精灵(sprite)
图片对象被叫做精灵图。你可以控制它们的位置,尺寸以及其他许多有用的可以制作交互式动画图形的属性。Pixi有一个 Sprite 类,它是创建游戏精灵的通用方式。
Pixi的loader对象可以用来做图片的预加载,position属性设置x、y坐标来精准还原设计稿,使用addChild添加到舞台。
//图片预加载
PIXI.loader
.add("https://p4.ssl.qhimg.com/t01f118b79154278482.png")
.add("https://p1.ssl.qhimg.com/t01495e763abe4d0ce9.png")
.on("progress", function(){
//do sth when loading
})
.load(loadingFinish);
//加载完成回调
function loadingFinish() {
//创建一个精灵
var sprite = new PIXI.Sprite(
PIXI.loader.resources["https://p1.ssl.qhimg.com/t01495e763abe4d0ce9.png"].texture
);
sprite.position.set(290, 297)
//添加到舞台
objPixiContainer.addChild(sprite)
//渲染到渲染器
pixiRender.render(objPixiContainer);
}
每次添加精灵或者精灵有动作之后,都需要重新去渲染舞台
function pageUpdate() {
requestAnimationFrame(pageUpdate);
pixiRender.render(objPixiContainer);
}
时差滚动
实现层次分明的背景动画效果,我们考虑用相对位移实现,原理是给不同的图层设置不同的运动系数,在用户划动幕布时,产生不相等的位移,从而在视觉上产生层次分明的效果。主要计算公式是:
// p.data.position.x为元素初始位置 ,p.data.speed为运动系数(不一致),distance为相对于初始位置的运动位移
p.x = p.data.position.x + (p.data.speed.x) * distance;
连续播放动画
使用PIXI.extras.AnimatedSprite来实现,并通过animationSpeed精准控制运动速度、play/stop控制播放/暂停、gotoAndStop精细到第几帧等来满足设计的动效需求
//声明AnimatedSprite的序列帧
var urlPadding = "https://p4.ssl.qhimg.com/d/inn/bdfcc19c48d8/",
act_animate_bg_img_arr = [];
for (let $e = 0; $e < 2; $e++) {
act_animate_bg_img_arr.push(urlPadding + "baba" + ($e + 1) + ".png");
}
//精灵
var sprite = new PIXI.extras.AnimatedSprite.fromImages(act_1_animate_bg_img_arr)
//精灵添加到舞台
objPixiContainer.addChild(sprite4)
//找到序列帧所在的图层,放到对应位置,并设置移动速度
sprite.animationSpeed = 0.1; //控制速度
sprite.play();//自动播放序列帧
多个场景管理
我们发现有多个场景,可以在objPixiContainer这个舞台下创建多个舞台来管理多个场景
//声明一个舞台
var stageContainer = new pixiContainer();
//多个场景(画布)
let scenes = [Act_1, Act_2, Act_3];
//每个场景又是一个舞台,依次渲染初始位置和舞台精灵
for (let j = 0; j < scenes.length; j++) {
scenes[j] = new pixiContainer();
scenes[j].pivot.set(act_sprites_list[j][0].position.x, 0);
scenes[j].position.set(act_sprites_list[j][0].position.x, 0);
}
//每个actContainerArr依次赋值初始位移和渲染背景
actContainerArr = scenes;
//todo 精灵渲染
//场景舞台添加到背景
objPixiContainer.addChild(stageContainer);
//每个场景添加到场景舞台
stageContainer.addChild(Act_1, Act_2, Act_3);
场景切换——ScrollerJS
多个画布,选择用ScrollerJs来实现画布的横向滚动效果,多个画布的管理,我们选择将数据抽象成对象,尽量使用循环实现来减少代码量。
思路
当用户手指滑动屏幕时,触发滚动,通过监听滚动事件,在滚动一定距离触发事件,事件参数包含位置信息等,根据位置信息决定渲染内容(画布显示隐藏,图片位置等)。
实现
首先,需要给scroller设置容器宽高限制:
scrollerObj.setDimensions(clientWidth, clientHeight, contentWidth, contentHeight);
然后实时监听鼠标滚动的距离:
//控制位移
function scrollerCallback(left, top, zoom) {
console.log(`当前滚动条位置:${left},${top}`)
//当前场景相对移动
stageContainer.position.x = -cur_X;
stageContainer.position.y = -cur_Y;
}
var objScroller = new Scroller(scrollerCallback, {
zooming: false,
animating: true,
bouncing: false,
animationDuration: 1000
});
objScroller.__enableScrollY = true;
我们还需要把滚动区域与渲染器关联起来
objPixiContainer.on("touchstart", onTouchStart)
.on("touchmove", onTouchMove)
.on("touchend", onTouchEnd);
function onTouchStart(e) {
var i = e.data.originalEvent;
isTouching = true;
objScroller.doTouchStart(i.touches, i.timeStamp);
}
function onTouchMove(e) {
if (isTouching) {
var i = e.data.originalEvent;
objScroller.doTouchMove(i.touches, i.timeStamp, i.scale);
}
}
function onTouchEnd(e) {
var i = e.data.originalEvent;
objScroller.doTouchEnd(i.timeStamp);
isTouching = false;
}
动画——TweenJS
动画效果实现思路:
- 可以回溯的动效使用AnimatedSprite + ScrollerJS结合相对位移逐帧渲染。
- 不能中断的使用 TweenJs 来填充实现。
tweenJS,类似于jQuery的animate方法和CSS3动画,定义起始、结束状态和时间,中间的过渡状态由TweenJS自动填充缓动效果
new TWEEN.Tween({ y: 34 })
.to({ y: 54 }, 1000)
.onUpdate(function () {
//
})
.start()
自动缩放
原来的写法
objPixiContainer.scale.set(screenScaleRito, screenScaleRito);
pixiRender.resize(windowWidth, windowHeight);//屏幕宽高
缩放造成图片模糊,改成resolution设置为2
objPixiContainer.scale.set(screenScaleRito, screenScaleRito);
pixiRender.resize(windowHeight / screenScaleRito, 720);//设计稿宽高
pixiRender.view.style.width = windowHeight / screenScaleRito + 'px';//设计稿宽高
pixiRender.view.style.height = 720 + 'px';