国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

用javascript寫(xiě)一個(gè)emoji表情插件

cheukyin / 3211人閱讀

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

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

demo: 我是demo
git : 我是項(xiàng)目git
下載地址: 點(diǎn)我下載

1. 事前準(zhǔn)備

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

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

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

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

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

模板界面簡(jiǎn)單,可以進(jìn)行自適應(yīng),并且兼容移動(dòng)端

盡可能只提供簡(jiǎn)單的api接口和方法,避免內(nèi)部涉及其他不是很相關(guān)的功能(如綁定某個(gè)特定的元素或者在內(nèi)部進(jìn)行數(shù)據(jù)傳輸?shù)鹊龋3植寮撵`活性等等

以上就是我們暫時(shí)能想到的功能和需求,下面就開(kāi)始寫(xiě)一個(gè)完整的插件了(當(dāng)然原生js插件某種程度來(lái)講使用起來(lái)相對(duì)比較自由,因?yàn)椴恍枰蕾嚹承┨囟◣?kù),而且也不需要按照某些庫(kù)類的格式標(biāo)準(zhǔn)進(jìn)行插件的編寫(xiě),但少了一些封裝好的方法也會(huì)使得插件寫(xiě)起來(lái)更費(fèi)力,至于怎么取舍就需要看個(gè)人需求來(lái)定了)

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

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

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

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

3. 填寫(xiě)配置文件

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

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

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

4. 插件開(kāi)寫(xiě)

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

首先我們需要?jiǎng)?chuàng)建一個(gè)對(duì)象(當(dāng)然你通過(guò)閉包來(lái)寫(xiě)也是可以的),明確好哪些數(shù)據(jù)和函數(shù)是可以共用的,哪些是不能共用的。就我個(gè)人的經(jīng)驗(yàn)來(lái)講,一般對(duì)于用來(lái)保存數(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;                          //每頁(yè)表情的行數(shù)
    this.callback = options.callback || function(){};     //當(dāng)表情被點(diǎn)擊時(shí)的回調(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)航欄的表情包的最大顯示個(gè)數(shù))

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

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

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

init : function(){
        //當(dāng)表情包的實(shí)際啟用個(gè)數(shù)大于設(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í)行接下來(lái)的工作

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(); },

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

__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;
            //將每個(gè)生成的表情包的容器放入content中
            content.appendChild(this.__initContent(key,emj)); 
            navRow.innerHTML += "
"+emj.name+"
"; } this.__initStyle(); this.wrapper.style.display = "block"; },

事件綁定:正常流程來(lái)走就行,注意某些地方需要用事件委托來(lái)提升性能,而這里沒(méi)用addEventListener是為了防止多次初始化init的時(shí)候?qū)е率录貜?fù)綁定,on+“event”事實(shí)上已經(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個(gè)一批)
            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)用接口即可,不管是初始化的時(shí)候還是事件觸發(fā)的時(shí)候,將表情包改變時(shí)會(huì)發(fā)生操作全都放一起,因?yàn)榇蟛糠植僮鞫际峭瑫r(shí)變化的,所以沒(méi)必要繼續(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();
},

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

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)";
}

最后一個(gè)是將code解釋成img的功能函數(shù)explain(): 大家通過(guò)前面的介紹可以知道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 "";
        });
},

基本上主要代碼就這么多了,還有一部分代碼可以看源代碼來(lái)了解,因?yàn)槲一旧隙加袑?xiě)注釋所以應(yīng)該不怎么難理解。

5. 結(jié)語(yǔ)

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/83065.html

相關(guān)文章

  • javascript寫(xiě)一個(gè)emoji表情插件

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

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

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

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

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

    ShowerSun 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<