從零打造線上版H5頁面生成器

小蟲巨蟹發表於2017-05-28

想必你一定使用過易企秀或其它微場景生成工具製作過炫酷的h5頁面,除了感嘆其神奇之處有沒有想過其實現方式呢?
從設計者的角度來看待問題,會有不一樣的收穫,本文將從零開始,使用node技術來設計實現一款精簡版的易企秀

Github: 傳送門
演示地址:傳送門

目錄

  1. 例項實現
  2. 例項模板化與渲染
  3. 前端視覺化操作
  4. 總結

一、例項實現

自動化/視覺化工具的實現,無不是大量重複例項的一種模板化,微場景生成工具也不例外,所以首先需要探討具體的一個微場景是怎麼實現的

來看下面這個例項:

從零打造線上版H5頁面生成器

要實現這樣的場景,我們首先得把任務分解下:

從零打造線上版H5頁面生成器

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作為模板引擎

我們先來分析下,微場景頁面的組成部分:

  1. 屏/頁(page-box):背景色、背景圖片、入場動畫和出場動畫
  2. 屏內專案:位置、寬高、透明度、動畫類、動畫持續時間、下一個動畫的間隔時間、圖片路徑/文字以及文字效果

於是資料模型可以定義為:

    //頁面資料
    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 // 去掉過多的空行
    }));複製程式碼

三、前端視覺化操作

在上一步模板化之後,我們已經可以根據資料和模板生成場景例項,那麼前端視覺化操作要做什麼就比較清晰了,前端視覺化操作的目的在於根據資料模型構造資料例項,前端的視覺化操作應該包括設定每一屏的背景色/背景圖片,並且可以增加或刪除一頁,在每一屏中可以增加文字或者圖片專案,並且設定對應的屬性

從零打造線上版H5頁面生成器
編輯屏

從零打造線上版H5頁面生成器
編輯動畫專案

在此採用了以上兩圖的設計風格:

  1. 頂部文字和圖片按鈕可以為當前page增加文字和圖片
  2. 左側控制屏/頁的增刪,以及切換當前編輯屏/頁
  3. 中部為編輯舞臺,顯示當前屏/頁的背景以及專案
  4. 右側為屬性設定皮膚,當屏/頁獲得焦點,編輯的是屏/頁屬性(背景和入場出場動畫),當動畫專案(文字或者圖片),編輯的是動畫專案的屬性

同樣的,我們要將任務分解:

  1. 控制page增刪與切換,屬性設定的Page.js
  2. 控制文字專案增刪,屬性設定的textItem.js
  3. 控制圖片專案增刪,屬性設定的imageItem.js
  4. 右側屬性設定皮膚控制rTab.js
  5. 拖拽控制ZResize.js
  6. 圖片上傳ZUpload.js

    這些個步驟每一步要寫的話,篇幅都可以長到獨立成文,例如,另一篇文章:div拖拽縮放jquery外掛編寫——帶8個控制點已經詳細說過,本文就不再擴充,如果有需要,可以私信我,有必要再整理

通過前端視覺化的操作,構造資料模型例項,傳入後臺,後臺再通過上一步的模板進行渲染,那麼整一個核心功能就走通了~~~

其它的入庫儲存,展示,預覽功能,則是需要慢慢豐富的過程

先把核心骨架搭建完畢,再去豐富皮肉,是一個軟體從無到有的重要心法

四、總結

本文重在說明設計步驟和實現思路,省去了很多實現細節,並非面面俱到,並且也不方便公佈所有原始碼,更細緻的討論,可以私信我,定盡力解答。

本文方法論:
1)任務分解
2)例項模板化
3)核心骨架搭建、再豐富皮肉功能

從零打造線上版H5頁面生成器
公眾號:菲麥前端

相關文章