成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

用javascript寫一個emoji表情插件

Stardustsky / 3332人閱讀

摘要:二是考慮到某些表情包現(xiàn)在可能并不想用,但代碼刪來刪去可能會很麻煩,所以提供了一個是否啟用的接口。

很久沒有寫文章了,說實話本人現(xiàn)在受困于五月病已經(jīng)快變成一條死咸魚了(T_T),本次就當(dāng)寫一個簡單的js插件教程了。本項目的代碼相對比較簡單,至于里面有些變量命名的問題就請你們不要吐槽了Σ(?д?lll)(好的,我承認(rèn)我英語就小學(xué)水平好吧。除了hello和goodbye其他的都不會了____orz)。 廢話就講到這里,下面開始正文。

demo: 我是demo
git : 我是項目git
下載地址: 點我下載

1. 事前準(zhǔn)備

事實上在寫一個插件前我們都需要事先想好你要實現(xiàn)哪些功能,怎么去實現(xiàn),這些大方向的東西是需要事先考慮的,至于具體細(xì)節(jié)和優(yōu)化選項我們可以在寫代碼的過程中再進行修改。

就以我們寫的這個emoji插件為例,網(wǎng)上已經(jīng)有一些相關(guān)的插件了,但你總感覺有些部分的需求不能被滿足(如:可以自行添加新的表情包而不用去改源代碼等等),這時我們就可以列出你想實現(xiàn)的功能項了:

需要滿足基本的表情插件的需求,包括圖片和對應(yīng)code的相互轉(zhuǎn)換

希望可以通過參數(shù)來調(diào)整每行以及每列表情圖片的顯示個數(shù),并且可以針對不同表情包多帶帶調(diào)整

希望用戶可以在不了解源代碼的情況下也能自行主動添加新的表情包

模板界面簡單,可以進行自適應(yīng),并且兼容移動端

盡可能只提供簡單的api接口和方法,避免內(nèi)部涉及其他不是很相關(guān)的功能(如綁定某個特定的元素或者在內(nèi)部進行數(shù)據(jù)傳輸?shù)鹊龋?,保持插件的靈活性等等

以上就是我們暫時能想到的功能和需求,下面就開始寫一個完整的插件了(當(dāng)然原生js插件某種程度來講使用起來相對比較自由,因為不需要依賴某些特定庫,而且也不需要按照某些庫類的格式標(biāo)準(zhǔn)進行插件的編寫,但少了一些封裝好的方法也會使得插件寫起來更費力,至于怎么取舍就需要看個人需求來定了)

2. 進行結(jié)構(gòu)劃分

當(dāng)我們正式開始代碼編寫的時候,當(dāng)然想自己寫出來的代碼不敢說很強勢,但至少結(jié)構(gòu)清晰,易于讀懂,而且代碼的性能也需要保證。這時我們就需要回到前面的需求了,由上面列出的5點可以看出,大部分的功能需求都是在我們程序內(nèi)部去實現(xiàn)的,唯一需要考慮的是上面的第3點。

這時我們可能已經(jīng)想到辦法了,比如說將新的表情包填好相關(guān)的參數(shù)后由接口傳入程序內(nèi)部去作處理。當(dāng)然這是一個合理的選擇,但考慮到代碼的復(fù)雜度和使用的簡易度,我們最好還是建立一個對應(yīng)config文件。因為首先這樣我們可以提供一些默認(rèn)的表情包,并且配置好相關(guān)的參數(shù)并注釋,后面的使用者只需要按照相關(guān)的格式復(fù)制然后修改就行了。而且將一些非邏輯性的數(shù)據(jù)多帶帶隔離開來有利于維持清晰的代碼結(jié)構(gòu),增加代碼的易讀性。所以到這里已經(jīng)可以基本上確定我們需要的文件了:

一個模板css文件; 2. 一個數(shù)據(jù)配置文件config.js; 3. 一個邏輯實現(xiàn)文件js;

3. 填寫配置文件

這里先填寫配置文件是為了有一個更明確的需求,以及防止在coding過程中忘記了某些需求(像我一樣,老了,腦袋不好使??(?′Д`?)?),當(dāng)然并不是所有插件都用配置文件比較好,新手請務(wù)必不要有這樣的誤區(qū),下面是我寫的配置代碼:

    var path = "http://localhost/wantEmoji/",  //項目所在的根地址
    emojis = {
        "paopao" : {
            "name" : "泡泡", //名字
            "col" : 10, //每一行最大的表情個數(shù)(建議填選的時候值不要太大或太小)
            "path" : path+"emojiSources/paopao/", //相對于項目根地址的路徑
            "enable" : true, //是否啟用本表情包
            "sources" : ["1.jpg"] //中間的值也支持{title:"笑",url:"1.jpg"}的形式,且可多帶帶設(shè)置
        }
    }

這部分代碼考慮了幾個點:
一是考慮到可能會在不同路徑的文件中調(diào)用同一個配置文件,所以為了保證路徑不出錯,需要確定每個包的絕對路徑值。
二是考慮到某些表情包現(xiàn)在可能并不想用,但代碼刪來刪去可能會很麻煩,所以提供了一個是否啟用的接口。
三是考慮到不同表情包的圖片尺寸可能不同,為了讓每張圖片盡可能清晰我們允許調(diào)整每行顯示的圖片個數(shù)(在程序中每個單項的size都是自動計算的)
四是考慮到每張表情圖片可能有的需要設(shè)置title來提示用戶這個表情是什么意思,所以允許sources項數(shù)組中的值可以為string也可以為object
最后也是主要考慮的問題,我們希望每個表情對應(yīng)的code值能夠自動生成而不是人為的對每個圖片去進行多帶帶設(shè)置,所以需要保證每個code的值都是唯一的,而且是容易被解析的。
這里emojis變量不是數(shù)組而是對象就是基于這個原因。 (我們最終生成的code值為[wem:emojis的key值_圖片名_圖片類型:wem]這種形式,如[wem:paopao_1_jpg:wem],表示的是paopao表情包里面的1.jpg)

4. 插件開寫

前面的準(zhǔn)備工作都做好后,現(xiàn)在我們終于可以開始寫真正的代碼了。雖然前面的內(nèi)容不怎么多,但對于一個插件乃至一個項目來說都是必不可少的一個步驟,特別是初學(xué)者,開始動手寫自己的插件時多想想該怎么做總是沒錯的。

首先我們需要創(chuàng)建一個對象(當(dāng)然你通過閉包來寫也是可以的),明確好哪些數(shù)據(jù)和函數(shù)是可以共用的,哪些是不能共用的。就我個人的經(jīng)驗來講,一般對于用來保存數(shù)據(jù)用的變量,最好都放在函數(shù)體內(nèi),而方法則都放在原型上。

var wantEmoji = function(options){
    options = options || {};
    var selector = options.wrapper || "body";  

    this.wrapper = document.querySelector(selector);    //包裹元素
    this.row = options.row || 4;                          //每頁表情的行數(shù)
    this.callback = options.callback || function(){};     //當(dāng)表情被點擊時的回調(diào),返回表情的code值

    this.emojis = window.emojis || emojis;        //加載表情包配置

    this.content = null;                   //.wEmoji-content
    this.navRow = null;                    //.wEmoji-row
    this.currentWrapper = null;         //.wEmoji-wrapper[data-choose="true"]

    this.activePage = 0;
    this.totalPage = 0;
    this.eachPartsNum = 4;                 //每一批顯示的表情包數(shù)(導(dǎo)航欄的表情包的最大顯示個數(shù))

    this.wrapWidth = 0;
    this.count = this.getEMJPackageCount();
    
    if(options.autoInit) //當(dāng)設(shè)置了autoInit之后會自動調(diào)用init函數(shù),默認(rèn)不會
    this.init();
};

上面的代碼我都加了注釋就不做細(xì)說了,下面是各個功能部分的實現(xiàn)(馬上就可以看到我英語捉急的地方了(`?ω?′))。

首先是init(): 完成某些數(shù)據(jù)的獲取以及確認(rèn)進入哪種情況

init : function(){
        //當(dāng)表情包的實際啟用個數(shù)大于設(shè)定值時,啟用.wEmoji-more
        if(this.count > this.eachPartsNum)
        this.wrapper.className += " wEmoji wEmoji-more";
        else
        this.wrapper.className += " wEmoji";

        this.wrapWidth = this.wrapper.clientWidth;

        this.initTemplete();
},

initTemplete(): 初始化模板,更新某些數(shù)據(jù)變量,并執(zhí)行接下來的工作

initTemplete : function(){

        var wrapper = this.wrapper,
            tpl = "
"+ "
<
"+ "
"+ "
"+ "
"+ "
>
"+ "
"+ "
"+ "
"+ "
"+ "
"; wrapper.innerHTML = tpl; this.content = wrapper.querySelector(".wEmoji-content"); this.navRow = wrapper.querySelector(".wEmoji-row"); this.__initData(); this.__bindEvent(); },

接下來是__initData():生成具體的表情圖片和導(dǎo)航等,這里需要注意的是進行dom操作時不要讓重排發(fā)生多次,使需要操作的dom元素脫離文檔流是減少重排的方法之一。另外這里還將許多屬性保存為臨時變量是為了提高程序性能(至于代碼優(yōu)化需要自己去找資料看,這里就簡單提一下)。

__initData : function(){
        var emojis = this.emojis,
            wrapper = this.wrapper,
            navRow = this.navRow,
            content = this.content,
            rowWidth = navRow.clientWidth,
            count = this.count;

        //減少重排
        wrapper.style.display = "none";

        content.innerHTML = "";
        navRow.style.width = count / this.eachPartsNum * 100 + "%";

        for( var key in emojis ){
            var emj = emojis[key];

            if(!emj.enable)
            continue;
            //將每個生成的表情包的容器放入content中
            content.appendChild(this.__initContent(key,emj)); 
            navRow.innerHTML += "
"+emj.name+"
"; } this.__initStyle(); this.wrapper.style.display = "block"; },

事件綁定:正常流程來走就行,注意某些地方需要用事件委托來提升性能,而這里沒用addEventListener是為了防止多次初始化init的時候?qū)е率录貜?fù)綁定,on+“event”事實上已經(jīng)夠用了。

__bindEvent : function(){
        var _self = this,
            wrapper = this.wrapper,
            row = this.navRow,
            pageBox = wrapper.querySelector(".wEmoji-pages"),
            prev = wrapper.querySelector(".wEmoji-prev-btn"),
            next = wrapper.querySelector(".wEmoji-next-btn"),
            content = this.content,
            down = "ontouchstart" in document ? "touchstart" : "mousedown",
            up = "ontouchend" in document ? "touchend" : "mouseup",
            move = "ontouchmove" in document ? "touchmove" : "mousemove",
            drag = false,
            x = 0;

        pageBox.onclick = function(e){
            e = e || event;
            var target = e.target || e.srcElement,
                idx = target.getAttribute("data-pageIdx");
            if(target.tagName.toLowerCase() != "li" || !idx){
                return false;
            }
            _self.showPage(idx-1);
        };

        row.onclick = function(e){
            e = e || event;
            var target = e.target || e.srcElement,
                eid = target.getAttribute("data-eid");

            if( eid && _self.emojis[eid] ){
                _self.chooseEmoji(eid);
                _self.showPage(0);
            }
        };

        var parts = Math.ceil(this.count / this.eachPartsNum), //可以將表情包數(shù)分為N批(默認(rèn)4個一批)
            partsIdx = 0,
            navWidth = wrapper.querySelector(".wEmoji-nav").clientWidth;

        prev.onclick = function(e){
            partsIdx = partsIdx - 1 < 0 ? 0 : partsIdx - 1;
            row.style.webkitTransform = "translateX("+(-partsIdx * navWidth)+"px) translateZ(0px)";
            row.style.transform = "translateX("+(-partsIdx * navWidth)+"px) translateZ(0px)";
        };

        next.onclick = function(e){
            partsIdx = partsIdx + 1 >= parts ? partsIdx : partsIdx + 1;
            row.style.webkitTransform = "translateX("+(-partsIdx * navWidth)+"px) translateZ(0px)";
            row.style.transform = "translateX("+(-partsIdx * navWidth)+"px) translateZ(0px)";
        };

        content.onclick = function(e){
            e = e || event;
            var target = e.target || e.srcElement,
                trueTarget = getTargetNode(target,".wEmoji-item"),
                emjCode;

            if(trueTarget)
            emjCode = trueTarget.getAttribute("data-emj");

            if(!emjCode)
            return false;

            _self.callback.call(_self,emjCode);
            console.log(emjCode);
        };

        content["on"+down] = function(e){
            e = e || event;
            drag = true;
            x = e.pageX || e.touches[0].pageX;
        };

        content["on"+move] = function(e){
            e = e || event;
            e.stopPropagation();
            e.preventDefault();
        };

        content["on"+up] = function(e){
            e = e || event;
            if(drag){
                drag = false;
                var endX = e.pageX || e.changedTouches[0].pageX,
                    dis = endX - x,
                    idx;

                if(dis > 50){
                    idx = Math.max(_self.activePage - 1,0);
                    _self.showPage(idx);
                } else if (dis < -50){
                    idx = Math.min(_self.activePage + 1,_self.totalPage - 1);
                    _self.showPage(idx);
                }
                x = 0;
            }
        };

},

下面是選擇表情包的功能chooseEmoji():封裝好后只需要調(diào)用接口即可,不管是初始化的時候還是事件觸發(fā)的時候,將表情包改變時會發(fā)生操作全都放一起,因為大部分操作都是同時變化的,所以沒必要繼續(xù)細(xì)分了。

chooseEmoji : function(eid){
        var navRow = this.navRow,
            content = this.content,
            targetWrapper = content.querySelector(".wEmoji-wrapper[data-eid=""+eid+""]"),
            targetList = navRow.querySelector(".wEmoji-list[data-eid=""+eid+""]"),
            chooseWrapper = content.querySelector(".wEmoji-wrapper[data-choose="true"]"),
            chooseList = navRow.querySelector(".wEmoji-list[data-choose="true"]");

        if(chooseWrapper){
            chooseList.setAttribute("data-choose","false");
            chooseWrapper.setAttribute("data-choose","false");
        }
        targetWrapper.setAttribute("data-choose","true");
        targetList.setAttribute("data-choose","true");

        this.currentWrapper = targetWrapper;
        this.__createPageList();
},

下面是頁面的切換showPage():完成初始化和事件觸發(fā)時頁面的切換

showPage : function(idx){
        this.activePage = idx;
        var wrapper = this.wrapper,
            currentWrapper = this.currentWrapper,
            pageTargetList = wrapper.querySelector(".wEmoji-page-list[data-pageIdx=""+(idx+1)+""]"),
            pageChoose = wrapper.querySelector(".wEmoji-page-list[data-choose="true"]");

        if(pageChoose)
        pageChoose.setAttribute("data-choose","false");
        pageTargetList.setAttribute("data-choose","true");

        currentWrapper.style.webkitTransform = "translateX("+(-this.wrapWidth*idx)+"px) translateZ(0px)";
        currentWrapper.style.transform = "translateX("+(-this.wrapWidth*idx)+"px) translateZ(0px)";
}

最后一個是將code解釋成img的功能函數(shù)explain(): 大家通過前面的介紹可以知道code的生成規(guī)則

explain : function(str){
        var reg = /[wem:(w+):wem]/g,
            _self = this;

        return str.replace(reg,function(str,target){
            var tempArr = target.split("_"),
                eid = tempArr.shift(),
                type = tempArr.pop(),
                name = tempArr.join("_");
                path = _self.emojis[eid].path;
                url = name+"."+type;

            return "";
        });
},

基本上主要代碼就這么多了,還有一部分代碼可以看源代碼來了解,因為我基本上都有寫注釋所以應(yīng)該不怎么難理解。

5. 結(jié)語

雖然我很想進一步把教程寫完全,但基于本人身體已經(jīng)被掏空的現(xiàn)實情況考慮,就不做打算了,效果的話可以點開上面的demo去看,大家有什么問題歡迎留言提問,以后會不定時寫一些插件,到時候也歡迎大家來捧場,以上(寫完要死了(? ° ? °)?)。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/112016.html

相關(guān)文章

  • javascript一個emoji表情插件

    摘要:二是考慮到某些表情包現(xiàn)在可能并不想用,但代碼刪來刪去可能會很麻煩,所以提供了一個是否啟用的接口。 很久沒有寫文章了,說實話本人現(xiàn)在受困于五月病已經(jīng)快變成一條死咸魚了(T_T),本次就當(dāng)寫一個簡單的js插件教程了。本項目的代碼相對比較簡單,至于里面有些變量命名的問題就請你們不要吐槽了Σ(?д?lll)(好的,我承認(rèn)我英語就小學(xué)水平好吧。除了hello和goodbye其他的都不會了____...

    cheukyin 評論0 收藏0
  • 程序員魚皮 表情包網(wǎng)站項目學(xué)習(xí)

    摘要:我們可以來看看數(shù)據(jù)庫這個字段就是判斷我們的這條數(shù)據(jù)是否是有效的,表示這條數(shù)據(jù)是有效的,表示這條數(shù)據(jù)是無效的。 ? 技術(shù)棧 后端 Java 8開發(fā)框架:SpringB...

    Yangder 評論0 收藏0
  • 如何實現(xiàn)emoji文本字?jǐn)?shù)計算?以及輸入框限制指定字符數(shù)?

    摘要:結(jié)合和簡單的正則就可以實現(xiàn)一個文本計算函數(shù)其實當(dāng)字符計算解決后,輸入框限制字符數(shù)就輕而易舉了。思路就是每次事件發(fā)生時,先判斷當(dāng)前字符數(shù)是否超過限制,如果超出,則用上一次的文本替換當(dāng)前輸入框的文本。 一個emoji文本用javascript該如何正確計算其文本長度?最容易想到的自然是用length來求長度。以下列舉常見emoji和復(fù)雜emoji。 // size: 2 ?.length ...

    ShowerSun 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<