如何拿react做一個(gè)codepen編輯器

折騰兩天也做出一個(gè)codepen的大概模型,目前只兼容了chrome。前端代碼用的react作為ui庫

分析codepen

首先要分析codepen的編輯器界面


codepen編輯器樣式
  • 紅色圈起來的地方是由codemirror完成的,只要看源碼就知道了
  • 藍(lán)色標(biāo)注的兩個(gè)拖拽bar,可以修改窗口大小

這兩條里 codemirror實(shí)現(xiàn)的只要引入就行了,主要難點(diǎn)就在如何做出一個(gè)可以任意調(diào)整分塊大小的編輯器容器。

設(shè)計(jì)模型

由codepen設(shè)計(jì)可以看出,每個(gè)拖拽的bar可以影響兩邊的窗口,當(dāng)其中一邊窗口大小不夠的時(shí)候,會影響那一邊相鄰的窗口。
假設(shè)有3個(gè)窗口 A,B,C 拖拽bar有 AB和BC。

  • 拖拽AB向右側(cè)移動的時(shí)候,A窗口會增加寬度,B窗口減小寬度,當(dāng)B窗口為0的時(shí)候,C窗口會相應(yīng)減少寬度,返回過來BC向左也是一樣的。

推廣到n個(gè)窗口的時(shí)候,情況是類似的。

  • 這里將拖拽的距離設(shè)置為消費(fèi)的能量cost
  • 拖拽bar其中一側(cè)會增加cost的寬度,另外一側(cè)會由一個(gè)或者多個(gè)窗口消費(fèi)cost。
  • 如果另一側(cè)不能完全消費(fèi)cost,那么增加寬度那側(cè) 也不能增加全部cost

按照上面的約束條件,我們就可以設(shè)計(jì)出拖拽代碼

/***
 *  widths 是一個(gè)數(shù)組 表示所有窗口的對應(yīng)寬度
 *  index  表示當(dāng)前拖拽bar的坐標(biāo),由1開始到widths.length-1;
 *  cost   是一次拖拽距離
 **/
function dragResize(widths, index, cost) {
    if (cost > 0) {  // 正向拖拽
        let noCost = cost;// 記錄有多少距離沒有消費(fèi)
        for (let i = index; i < widths.length; i++) {
            noCost = calcuCost(widths, i, noCost);
            if (noCost == 0) break;
        }
        //另一側(cè)窗口的增加距離為 總消耗 - 未消耗
        widths[index - 1] += cost - noCost;
    } else if (cost < 0) { // 反向拖拽
        cost = -cost;
        let noCost = cost;// 記錄有多少距離沒有消費(fèi)
        for (let i = index - 1; i > -1; i--) {
            noCost = calcuCost(widths, i, noCost);
            if (noCost == 0) break;
        }
        //另一側(cè)窗口的增加距離為 總消耗 - 未消耗
        widths[index] += cost - noCost;
    }
}

/**
 *  計(jì)算消耗 并返回剩余未消耗
 */
function calcuCost(widths, index, noCost) {
    let result = widths[index] - noCost;
    if (result <= 0) {
        // 記錄窗口的消費(fèi) 如果result<=0表示這個(gè)窗口不能消耗完全
        noCost = -result; // 剩余多少沒有被消費(fèi)
        widths[index] = 0;// 由于消耗不完 所以它的最后寬度變?yōu)?
        return noCost;
    } else {
        //記錄窗口的消費(fèi) 如果result>0 表示到這個(gè)窗口已經(jīng)完全消費(fèi)了拖拽距離
        widths[i] = result;// 窗口設(shè)置為剩余的寬度
        return 0;
    }
}

其實(shí)上下拖拽也是同理的,只要把widths換成height就可以了。

瀏覽器拖拽工具類

正常拖拽設(shè)計(jì)流程就是 mousedown,mousemove,mouseup。mousedown的時(shí)候開始監(jiān)聽mousemove,mouseup的時(shí)候停止監(jiān)聽。

  • 如果監(jiān)聽元素mousemove 鼠標(biāo)移出元素之后就會產(chǎn)生事件中斷的問題
    所以這里mousemove和mouseup都監(jiān)聽的document的事件。
  • 監(jiān)聽document的mousemove 鼠標(biāo)移出到瀏覽器窗口外 ,也會產(chǎn)生事件中斷的問題。
    所以可以采用captureEvents。只要調(diào)用了captureEvents 無論鼠標(biāo)是否在窗口內(nèi)都可以監(jiān)聽到。

拖拽所關(guān)心的事情

  • 每次移動 鼠標(biāo)當(dāng)前位置與上次位置的距離變化 dx,dy
  • 每次移動 從鼠標(biāo)按下到現(xiàn)在總位置變化 px, py
  • 何時(shí)停止移動
/**
 *   拖拽中回調(diào)函數(shù)
 *   dx  鼠標(biāo)在x軸的變化距離
 *   dy  鼠標(biāo)在y軸的變化距離
 *   px  鼠標(biāo)從開始移動 到現(xiàn)在的總x軸變化距離
 *   py  鼠標(biāo)從開始移動 到現(xiàn)在的總y軸變化距離
 **/
function onDrag(dx,dy,px,py) {
}

/**
 *   拖拽結(jié)束回調(diào)函數(shù)
 *   dx  鼠標(biāo)在x軸的變化距離
 *   dy  鼠標(biāo)在y軸的變化距離
 *   px  鼠標(biāo)從開始移動 到現(xiàn)在的總x軸變化距離
 *   py  鼠標(biāo)從開始移動 到現(xiàn)在的總y軸變化距離
 **/
function onDragEnd(dx,dy,px,py) {
}


/**
 *  拖拽函數(shù) 調(diào)用時(shí)機(jī)需要在鼠標(biāo)按下時(shí)候
 *  startX 起始鼠標(biāo)x軸坐標(biāo)
 *  startY 起始鼠標(biāo)y軸坐標(biāo)
 *  onDrag 拖拽中監(jiān)聽函數(shù)
 *  onDragEnd 拖拽結(jié)束監(jiān)聽函數(shù)
 */
function drag(startX, startY, onDrag, onDragEnd) {
    let prevX = startX;
    let prevY = startY;

    function mouseMove(e) {
        onDrag(e.pageX - prevX, e.pageY - prevY, e.pageX - startX, e.pageY - startY);
        prevX = e.pageX;
        prevY = e.pageY;
    }

    function mouseUp(e) {
        onDragEnd(e.pageX - prevX, e.pageY - prevY, e.pageX - startX, e.pageY - startY);
        document.releaseEvents(Event.MOUSEMOVE | Event.MOUSEUP);
        document.removeEventListener('mousemove', mouseMove);
        document.removeEventListener('mouseup', mouseUp);
    }

    document.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
    document.addEventListener('mousemove', mouseMove);
    document.addEventListener('mouseup', mouseUp);
}

效果圖

這里貼一張效果圖。


效果圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 總結(jié): 鼠標(biāo)事件 1.click與dbclick事件$ele.click()$ele.click(handler(...
    阿r阿r閱讀 1,714評論 2 10
  • (續(xù)jQuery基礎(chǔ)(1)) 第5章 DOM節(jié)點(diǎn)的復(fù)制與替換 (1)DOM拷貝clone() 克隆節(jié)點(diǎn)是DOM的常...
    凜0_0閱讀 1,499評論 0 8
  • 因?yàn)橐鲆粋€(gè)地圖操作的項(xiàng)目,需要用到這個(gè)地圖庫,但是查詢官方API麻煩,而且這個(gè)地圖框架的API做的用起來確實(shí)太麻...
    虛幻的銹色閱讀 34,252評論 1 15
  • 本節(jié)介紹各種常見的瀏覽器事件。 鼠標(biāo)事件 鼠標(biāo)事件指與鼠標(biāo)相關(guān)的事件,主要有以下一些。 click 事件,dblc...
    許先生__閱讀 2,827評論 0 4
  • 近日,網(wǎng)易的生存對戰(zhàn)題材手游《荒野行動》進(jìn)行了一次更新,更新后游戲世界觀和相關(guān)的細(xì)節(jié)設(shè)定進(jìn)行了全面的迭代。同時(shí)加入...
    雪糕游戲社閱讀 360評論 0 1

友情鏈接更多精彩內(nèi)容