想必你一定使用過易企秀或其它微場景生成工具製作過炫酷的h5頁面,除了感嘆其神奇之處有沒有想過其實現方式呢?
從設計者的角度來看待問題,會有不一樣的收穫,本文將從零開始,使用node技術來設計實現一款精簡版的易企秀
目錄
- 例項實現
- 例項模板化與渲染
- 前端視覺化操作
- 總結
一、例項實現
自動化/視覺化工具的實現,無不是大量重複例項的一種模板化,微場景生成工具也不例外,所以首先需要探討具體的一個微場景是怎麼實現的
來看下面這個例項:
要實現這樣的場景,我們首先得把任務分解下:
1) 佈局:整個場景可以分為很多屏(頁),每一屏上有動畫元素(文字/圖片)
<!-- --------------html---------- -->
<div class="page-box" id="page1">
<div class="item page1-item1">
...
</div>
<div class="item page1-item2">
...
</div>
<div class="pre-item">
...
</div>
</div>
<div class="page-box" id="page2">
<div class="item page2-item1" data-cls="bounceIn1000" data-ts="0">
...
</div>
<div class="item page2-item2" data-cls="bounceIn1000" data-ts="1000">
...
</div>
<div class="pre-item">
...
</div>
</div>
<!-- --------------css---------- -->
// 每一屏(page-box)都是absolute佈局,便於為每屏增加入場出場動畫(減少重繪重排)
// 每一屏寬高皆為100%,預設隱藏,入場增加入場動畫,並設定為可見
.page-box {
position: absolute;
top: 0;
z-index: 1;
width: 100%;
height: 100%;
visibility: hidden;
background: no-repeat center center #FFFFFF;
background-size: 100% 100%;
}
.page-box.show {
visibility: visible;
}
// 屏內動畫專案,也都採用abasolute佈局,just因為,是動畫元素
.item {
position: absolute;
background-repeat: no-repeat;
opacity: 0;
text-align: center;
}複製程式碼
2) 屏間切換:事件觸發,過渡動畫
/**
* 下滑手勢,頁面上翻
* 上一屏(prev)增加入場動畫slideZoom_tInt,本屏增加離場動畫slideZoom_tOut
*/
pre: function() {
var $el = $(".page-box.show");
var $prev = $el.prev(".page-box")[0] ? $el.prev() : $(".page-box").last();
$prev.addClass("show").show().css({
"-webkit-animation":"slideZoom_tInt 0.5s linear",
"animation":"slideZoom_tInt 0.5s linear"
});
$el.css({
"-webkit-animation":"slideZoom_tOut 0.5s linear",
"animation":"slideZoom_tOut 0.5s linear"
});
setTimeout(function(){
$el.removeClass("show");
...
},500);
}
/**
* 滑動事件監聽
*/
//控制滑動
//不禁止預設事件 他丫的不能滑
document.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
$(document).on('swipeUp', function() {
slider.next();
}).on('swipeDown', function() {
//手勢下滑,向上翻頁
slider.pre();
});複製程式碼
3) 屏內專案控制(文字和圖片):引入動畫庫animate.css來設定屏內專案的動畫效果(所以說是精簡版易企秀)
/**
* 動畫效果控制,動畫效果播放、時間間隔
*/
//動畫一個個播放,遞迴呼叫
var i = 0;
function startShow(callback) {
if(i === itemLength){
callback && callback();
return ;
}
var item = items.eq(i);
var cls = item.attr('data-cls');//動畫類
var ts = item.attr('data-ts');//下一個動畫開始間隔時間
item.addClass(cls);
i++;
setTimeout(function() {
startShow(callback);
},ts);
};
//播放某一屏的動畫
function showItem(id, callback) {
var self = this;
i = 0;
items = $('#' + id + " .item");
itemLength = items.length;
if(!callback) {
callback = function() {
self.showPreItem(id);
}
}
startShow(callback);
},複製程式碼
4) 頁面載入進度控制:阻塞頁面載入進度的無非圖片,列舉頁面使用到的所有圖片(可以使用程式遍歷目錄),判斷Image物件的complete狀態或者監聽onload事件來判斷圖片是否載入完成,篇幅原因,程式碼就不貼出來了
二、例項模板化與渲染
所謂模板化,無非是在重複例項中提取共性的東西定義資料模型,並且使用模板標籤來描述,渲染的過程中用資料填充模板佔位符
在此選用了nunjucks作為模板引擎
我們先來分析下,微場景頁面的組成部分:
- 屏/頁(page-box):背景色、背景圖片、入場動畫和出場動畫
- 屏內專案:位置、寬高、透明度、動畫類、動畫持續時間、下一個動畫的間隔時間、圖片路徑/文字以及文字效果
於是資料模型可以定義為:
//頁面資料
pages: [
{
burl: String, //背景圖片
inAnimate: String,
outAnimate: String,
bgColor: String,
items: [ //頁面專案
{
px: String, //位置x 必選
py: String, //位置y 必選
width: String, //寬度 必選
height: String, //高度 必選
transparent: String,//透明度 可選
animateClass: String,//動畫 可選
animateDuration: String, //動畫持續時間,預設2000
nextAnimateTime: String, //下一個item動畫開始的時間間隔
zIndex: String, //可選
imgUrl: String,// 圖片路徑 //可選
text: String, //文字
textStyle: {
'color': String,
'font-size': String
}
}
...
]
},
{
...
}
...
]複製程式碼
根據資料模型來編寫模板
<!-- ---------html模板片段--------- -->
//遍歷pages生成每一屏的div
//遍歷page的items生成動畫專案div
{% set i = 0 %}
{% for page in pages %}
{% set i = i+1 %}
{% set j = 0 %}
<div class="page-box" id="page{{i}}">
{% for item in page.items %}
{% set j = j+1 %}
<div class="item page{{i}}-item{{j}}" data-cls="{{ item.animateClass }}{{item.animateDuration}}" {% if item.nextAnimateTime %} data-ts="{{ item.nextAnimateTime }}" {% else %} data-ts="500" {% endif %} >
{% if item.text %}
{{ item.text }}
{% endif %}
{% if item.imgUrl %}
![]({{ item.imgUrl }})
{% endif %}
</div>
{% endfor %}
<div class="pre-item"><div class="pre-wrap"><div class="pre-box1"><div class="pre1"></div></div><div class="pre-box2"><div class="pre2"></div></div></div></div>
</div>
{% endfor %}
<!-- ---------css模板片段--------- -->
/************* item style ***********************/
{% set i = 0 %}
{% for page in pages %}
{% set i = i+1 %}
{% set j = 0 %}
#page{{i}} {
{% if page.burl %}
background-image: url({{page.burl}});
{% endif %}
background-color: {{page.bgColor}};
}
{% for item in page.items %}
{% set j = j+1 %}
.page{{i}}-item{{j}} {
width: rem({{item.width}});
height: rem({{item.height}});
left: rem({{item.px}});
top: rem({{item.py}});
{% for key, value in item.textStyle %}
{{key}}: {{value}};
{% endfor %}
}
{% endfor %}
{% endfor %}
//這裡要重點說明的,資料模型中的所有item對應的,動畫類和持續時長作為key值
//去重之後,構造animateClasses物件,遍歷該物件,生成相應的動畫類
//例如動畫類bounceIn1000:就表示動畫庫中的bounceIn,並且持續時長1000毫秒
/**************** animate class **************************/
{% for cname, cvalue in animateClasses %}
.{{cname}} {
-webkit-animation: {{cvalue.ac}} {{cvalue.ad}}s ease 1 both;
-webkit-animation-play-state: initial;
animation: {{cvalue.ac}} {{cvalue.ad}}s ease 1 both;
animation-play-state: initial;
opacity: {% if cvalue.isOut %}0 {% else %} 1 {% endif %};
}
{% endfor %}複製程式碼
渲染過程:
1) 複製用到的圖片
2) rem預處理
3) 根據資料渲染模板
/**************build程式碼片段*************************/
//解析view
var content = htmlTemplate.render('view.html', config);
utils.createFile(target + '/view.html', beautify_html(content, {
'max_preserve_newlines': 0 // 去掉過多的空行
}));
//解析樣式
var styleContent = htmlTemplate.render('css/style.css', config);
utils.createFile(target + '/css/style.css', beautify_css(px2rem(styleContent), {
'max_preserve_newlines': 1 // 去掉過多的空行
}));
//解析main.js
var mainJsContent = htmlTemplate.render('js/main.js', {
list: images
});
utils.createFile(target + '/js/main.js', beautify_js(mainJsContent, {
'max_preserve_newlines': 1 // 去掉過多的空行
}));複製程式碼
三、前端視覺化操作
在上一步模板化之後,我們已經可以根據資料和模板生成場景例項,那麼前端視覺化操作要做什麼就比較清晰了,前端視覺化操作的目的在於根據資料模型構造資料例項,前端的視覺化操作應該包括設定每一屏的背景色/背景圖片,並且可以增加或刪除一頁,在每一屏中可以增加文字或者圖片專案,並且設定對應的屬性
在此採用了以上兩圖的設計風格:
- 頂部文字和圖片按鈕可以為當前page增加文字和圖片
- 左側控制屏/頁的增刪,以及切換當前編輯屏/頁
- 中部為編輯舞臺,顯示當前屏/頁的背景以及專案
- 右側為屬性設定皮膚,當屏/頁獲得焦點,編輯的是屏/頁屬性(背景和入場出場動畫),當動畫專案(文字或者圖片),編輯的是動畫專案的屬性
同樣的,我們要將任務分解:
- 控制page增刪與切換,屬性設定的Page.js
- 控制文字專案增刪,屬性設定的textItem.js
- 控制圖片專案增刪,屬性設定的imageItem.js
- 右側屬性設定皮膚控制rTab.js
- 拖拽控制ZResize.js
- 圖片上傳ZUpload.js
這些個步驟每一步要寫的話,篇幅都可以長到獨立成文,例如,另一篇文章:div拖拽縮放jquery外掛編寫——帶8個控制點已經詳細說過,本文就不再擴充,如果有需要,可以私信我,有必要再整理
通過前端視覺化的操作,構造資料模型例項,傳入後臺,後臺再通過上一步的模板進行渲染,那麼整一個核心功能就走通了~~~
其它的入庫儲存,展示,預覽功能,則是需要慢慢豐富的過程
先把核心骨架搭建完畢,再去豐富皮肉,是一個軟體從無到有的重要心法
四、總結
本文重在說明設計步驟和實現思路,省去了很多實現細節,並非面面俱到,並且也不方便公佈所有原始碼,更細緻的討論,可以私信我,定盡力解答。
本文方法論:
1)任務分解
2)例項模板化
3)核心骨架搭建、再豐富皮肉功能