使用canvas實現畫中畫效果的H5


最近看到一個挺有趣的H5,主要效果就是通過不斷的放縮來展示畫中畫,網上找了一下並沒有這方面的實現代碼,故決定原創一下,並分享出來

主要的思路就是通過canvas不斷的寫入圖片,考慮到每一層的圖片的位置和大小不一樣,於是通過最外層的圖片來尺寸和位置來控制里面的圖片,然后通過循環寫入canvas的方式實現最終的效果。

需要注意的是使用canvas寫入圖片需要預加載圖片,通過回調函數來寫入,同時,由於圖片加載需要一定的時候,所以一般的H5會有一個加載的過程,我也加上了一個回調函數來處理加載圖片以后其他的操作,比如隱藏加載頁面等操作。

此項目已經放到碼雲上了,傳送門,第一次分享項目,共勉之。

主要代碼如下:
let $ = require('jquery');
let tools = require('./utils.js');
$.fn.longPress = function() {
    let timeout = undefined;
    let start;
    let beginHandel, endHandel;
    let length = arguments.length;
    beginHandel = arguments[0];
    if (length = 2) {
        endHandel = arguments[1];
    }
    $(this).on('touchstart', function(e) {
        e.preventDefault();
        timeout = setTimeout(beginHandel, 500);
    });
    $(this).on('touchend', function(e) {
        e.preventDefault();
        tools.execCB(endHandel);
        clearTimeout(timeout);
    });
};
let TWEEN = require('@tweenjs/tween.js');
(function(global, factory) {
    if (typeof define === 'function' && define.amd) {
        define(function() {
            return factory(global, global.document);
        })
    } else if (typeof module !== 'undefined' && module.exports) {
        module.exports = factory(global, global.document);
    } else {
        global.Pip = factory(global, global.document);
    }
}(typeof window !== 'undefined' ? window : this, function(window, document) {

    'use strict'
    //常量
    const CW = $(window).width();
    const CH = $(window).height();
    let container = $('#pip');


    let Pip = function(options) {
        let _this = this;
        this.defaultOps = {
            model: 'bts',
            duration: 1000,
        }
        if (typeof options == 'object') {
            Object.keys(options).map(function(key) {
                _this.defaultOps[key] = options[key];
            })
        }
        //創建canvas
        let canvas = document.createElement('canvas');
        let length = _this.defaultOps.content.length;
        canvas.width = CW;
        canvas.height = CH;
        _this.defaultOps.context = canvas.getContext('2d');
        container.append(canvas);

    };
    Pip.prototype.start = function(cb) {
        let opts = this.defaultOps;
        let ctx = opts.context;
        let content = opts.content;
        let length = content.length;
        let model = opts.model;
        let beginStatus = {};
        let endStatus = {};
        let i = 0;

        if (ctx) {
            // 加載所有的圖片
            loadimages(content, function(images) {
                //去掉加載提示頁面
                tools.execCB(cb)

                // 總時間
                let duration = opts.duration;
                let timestamp1 = 0
                let timestamp2 = 0

                // 補間動畫
                let tween = null
                // 初始化狀態
                if (model == 'stb') {
                    i = length - 2;
                    let width = CW;
                    let height = CH;
                    let left = 0;
                    let top = 0;

                    for (let k = i + 1; k >= 0; k--) {
                        if (k !== i + 1) {
                            left = left + width * content[length - 1 - k].left;
                            top = top + height * content[length - 1 - k].top;
                            width = width * content[length - 1 - k].width;
                            height = height * content[length - 1 - k].height;
                        }
                        let image = images[length - 1 - k];
                        ctx.drawImage(image, left, top, width, height);
                    }
                    //定義開始和結束狀態
                    endStatus = {
                        width: CW / content[length - 1 - i].width,
                        height: CH / content[length - 1 - i].height,
                        left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                        top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                    }
                    beginStatus = {
                        width: CW,
                        height: CH,
                        left: 0,
                        top: 0
                    }
                } else {
                    ctx.drawImage(images[length - 1 - i], 0, 0, container.width(), container.height());
                    //定義開始和結束狀態
                    beginStatus = {
                        width: CW / content[length - 1 - i].width,
                        height: CH / content[length - 1 - i].height,
                        left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                        top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                    }
                    endStatus = {
                        width: CW,
                        height: CH,
                        left: 0,
                        top: 0
                    }

                }






                function go() {
                    tween = new TWEEN.Tween(beginStatus)
                        .to(endStatus, duration)
                        .onUpdate(function() {
                            let _this = this;
                            ctx.clearRect(0, 0, container.width(), container.height());
                            let width = _this.width;
                            let height = _this.height;
                            let left = _this.left;
                            let top = _this.top;

                            for (let k = i + 1; k >= 0; k--) {
                                if (k !== i + 1) {
                                    left = left + width * content[length - 1 - k].left;
                                    top = top + height * content[length - 1 - k].top;
                                    width = width * content[length - 1 - k].width;
                                    height = height * content[length - 1 - k].height;
                                }
                                let image = images[length - 1 - k];
                                ctx.drawImage(image, left, top, width, height)
                            }
                        })
                        .onComplete(function() {
                            if (model == 'bts') {
                                i++;
                            } else {
                                i--;
                            }
                            if (i > length - 2 || i < 0) {
                                if (model == 'bts') {
                                    i--;
                                    model = 'stb';
                                    endStatus = {
                                        width: CW / content[length - 1 - i].width,
                                        height: CH / content[length - 1 - i].height,
                                        left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                                        top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                                    };
                                    beginStatus = {
                                        width: CW,
                                        height: CH,
                                        left: 0,
                                        top: 0
                                    };
                                } else {
                                    i++;
                                    model = 'bts';
                                    beginStatus = {
                                        width: CW / content[length - 1 - i].width,
                                        height: CH / content[length - 1 - i].height,
                                        left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                                        top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                                    };
                                    endStatus = {
                                        width: CW,
                                        height: CH,
                                        left: 0,
                                        top: 0
                                    };
                                }
                                return;
                            }
                            //定義開始和結束狀態
                            if (model == 'bts') {
                                beginStatus = {
                                    width: CW / content[length - 1 - i].width,
                                    height: CH / content[length - 1 - i].height,
                                    left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                                    top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                                };
                                endStatus = {
                                    width: CW,
                                    height: CH,
                                    left: 0,
                                    top: 0
                                };
                            } else {
                                endStatus = {
                                    width: CW / content[length - 1 - i].width,
                                    height: CH / content[length - 1 - i].height,
                                    left: -CW / content[length - 1 - i].width * content[length - 1 - i].left,
                                    top: -CH / content[length - 1 - i].height * content[length - 1 - i].top
                                };
                                beginStatus = {
                                    width: CW,
                                    height: CH,
                                    left: 0,
                                    top: 0
                                };

                            }

                            duration = opts.duration;
                            go();
                        })
                        .onStop(function() {
                            timestamp2 = new Date().getTime()
                            duration = duration - (timestamp2 - timestamp1)
                            if (duration < 0) {
                                duration = 0
                            }
                        })
                        .onStart(function() {
                            timestamp1 = new Date().getTime()
                        })
                        .start()

                    requestAnimationFrame(animate)

                    function animate() {
                        TWEEN.update()
                        requestAnimationFrame(animate)
                    }
                }

                container.longPress(function() {
                    go()
                }, function() {
                    if (tween) {
                        tween.stop()
                    }
                })
            })
        } else {
            console.log("context is not defined")
            return
        }

        function loadimages(content, cb) {
            let images = [];
            let num = 0;
            let length = content.length;
            for (let c in content) {
                images[c] = new Image();
                images[c].onload = function() {
                    if (num === length) {
                        num = 0;
                        tools.execCB(cb, images)
                    }
                }
                images[c].src = content[length - 1 - num].src;
                num++;
            }
        }
    };
    return Pip
}));
用法如下:
var pip = new Pip({
    content: [{
        src: require('../src/images/2.jpg'),
        top: 0,
        left: 0,
        width: 1,
        height: 1
    }, {
        src: require('../src/images/3.png'),
        top: .1,
        left: .05,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/4.jpg'),
        top: .4,
        left: .6,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/5.jpg'),
        top: .4,
        left: .6,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/6.jpg'),
        top: .9,
        left: .4,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/7.jpg'),
        top: .1,
        left: .05,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/8.jpg'),
        top: .4,
        left: .6,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/9.jpg'),
        top: .1,
        left: .05,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/4.jpg'),
        top: .4,
        left: .6,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/5.jpg'),
        top: .4,
        left: .6,
        width: .08,
        height: .08
    }, {
        src: require('../src/images/6.jpg'),
        top: .9,
        left: .4,
        width: .08,
        height: .08
    }],
    model: 'stb',
});
pip.start(function() {
    //此處加載完圖片以后隱藏遮罩
    $('.mask').hide();
});


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM