<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          活動可視化搭建(拖拽生成頁面)

          共 2414字,需瀏覽 5分鐘

           ·

          2021-01-17 19:34

          授權(quán)轉(zhuǎn)載自:石頭人漢考克,https://juejin.cn/post/6844904083120193543

          前言

          公司經(jīng)常為了活動推廣營銷,拉新留存,制作臨時活動頁面,且組件大體相似,為了提高運(yùn)營的工作效率,減少開發(fā)成本,基于此開發(fā)一個活動可視化搭建項目,讓運(yùn)營可以通過,點(diǎn)擊和拖拽組件,選擇或?qū)霐?shù)據(jù)的的方式,快速生成活動頁面上線,在此做一個小小的總結(jié)。

          核心設(shè)計

          大體流程是:

          創(chuàng)建 -> 編輯 -> 保存 -> 發(fā)布 -> 展示

          核心:

          維護(hù)一個obj,保存著個組件的父子關(guān)系,像一個node樹,每個組件都有唯一的ID,舉例如下

          const?nodeTree?=?{
          ??id:?'component0',
          ??name:?'rootContainer',
          ??children:?[
          ??????{
          ??????????id:?'component1',
          ??????????name:?'header'
          ??????},
          ??????{
          ??????????id:?'component2',
          ??????????name:?'content',
          ??????????children:?[],
          ??????}
          ??]
          }

          1. 創(chuàng)建一個obj,編輯時 不操作dom,就是增刪改查obj數(shù)組,來更新視圖

          2. 保存時obj存在數(shù)據(jù)庫,在服務(wù)器某個地址生成html文件,靜態(tài)資源, obj通過模版?zhèn)鬟f掛載在window上,并生成唯一訪問路徑

          3. 發(fā)布時改變當(dāng)前活動頁面可訪問狀態(tài)

          4. 展示時,根據(jù)obj渲染指定的定制組件生成頁面

            重點(diǎn)

            1.節(jié)點(diǎn)操作

            不操作dom節(jié)點(diǎn),通過對數(shù)組對象的增刪改查來更新視圖

            2.拖拽與判定

            編輯時,涉及到拖拽,判斷點(diǎn)與矩形相交,設(shè)置偏移量,來區(qū)分同級插入,或子級插入,以及提示信息

          • 拖拽:也不是完全利用HTML5 拖放(Drag 和 drop)事件,而是用其監(jiān)聽用戶操作,在dragStart(拖動開始),dragOver(拖動到可釋放區(qū)),dragEnd(拖動結(jié)束) ,drop(放下)時進(jìn)行相應(yīng)的數(shù)據(jù)傳遞和增刪改查的動作
          • 判斷點(diǎn)與矩形相交:當(dāng)拖拽一個組件懸停到可放置組件區(qū)域,用戶可能是想放在懸停組件的上面,下面,左面,右面,里面五種可能(塊級元素為上下里,行級元素為左右里)我們需要為多種選擇劃分相應(yīng)的區(qū)域,和明確的提示

            重點(diǎn)邏輯

          export?const?relativePositionJudge?=?(cur,?box,?isDrop,?direction)?=>?{
          ?const?rect?=?box.getBoundingClientRect();
          ?let?offset?=?null;
          ?if(direction)?{
          ?????offset?=?{
          ?????????x:?rect.width?/?2,
          ?????????y:?0,
          ?????};
          ?}?else?{
          ?????offset?=?{
          ?????????x:?0,
          ?????????y:?isDrop???rect.height?/?4?:?rect.height?/?2,
          ?????};
          ?}
          ?const?point?=?{
          ?????x:?cur.clientX,
          ?????y:?cur.clientY,
          ?};
          ?//?up
          ?const?rect1?=?{
          ?????x:?rect.x?+?offset.x,
          ?????y:?rect.y,
          ?????w:?rect.width?-?(offset.x?*?2),
          ?????h:?offset.y,
          ?};
          ?//?down
          ?const?rect2?=?{
          ?????x:?rect.x?+?offset.x,
          ?????y:?(rect.y?+?rect.height)?-?offset.y,
          ?????w:?rect.width?-?(offset.x?*?2),
          ?????h:?offset.y,
          ?};
          ?//?inside
          ?const?rect3?=?{
          ?????x:?rect.x?+?offset.x,
          ?????y:?rect.y?+?offset.y,
          ?????w:?rect.width?-?(offset.x?*?2),
          ?????h:?rect.height?-?(offset.y?*?2),
          ?};
          ?//?front
          ?const?rect4?=?{
          ?????x:?rect.x,
          ?????y:?rect.y,
          ?????w:?offset.x,
          ?????h:?rect.height,
          ?};
          ?//?behind
          ?const?rect5?=?{
          ?????x:?rect.x?+?rect.width?-?offset.x,
          ?????y:?rect.y,
          ?????w:?offset.x,
          ?????h:?rect.height,
          ?};
          ?let?pos?=?null;
          ?if?(pointInRect(point,?rect1))?pos?=?'up';
          ?if?(pointInRect(point,?rect2))?pos?=?'down';
          ?if?(pointInRect(point,?rect3))?pos?=?'inside';
          ?if?(pointInRect(point,?rect4))?pos?=?'front';
          ?if?(pointInRect(point,?rect5))?pos?=?'behind';
          ?return?pos;
          };

          const?pointInRect?=?(point,?rect)?=>?{
          ?return?point.x?>=?rect.x?&&?point.y?>=?rect.y?&&?point.x?<=?rect.x?+?rect.w?&&?point.y?<=?rect.y?+?rect.h;
          };

          1. cur為當(dāng)前拖拽的組件,可通過其獲取鼠標(biāo)當(dāng)前坐標(biāo)
          2. box為當(dāng)前懸停區(qū)域,通過getBoundingClientRect方法獲取寬高及位置
          3. isDrop為當(dāng)前區(qū)域是否可放置,direction 為當(dāng)前區(qū)域元素的排列方向,通過兩者設(shè)置,橫向或縱向的偏移量大小,
          4. 假如當(dāng)前區(qū)域縱向排列,且可放置,則把可放置區(qū)域由上至下分成3份,1/4,1/2,1/2(具體偏移量可按照需求或用戶體驗(yàn)自由設(shè)置)
          5. 通過鼠標(biāo)移動當(dāng)前坐標(biāo)與分份后區(qū)域四個角坐標(biāo)比較,確定位置,進(jìn)而做放置提示和節(jié)點(diǎn)插入

          請看下圖演示

          3.組件與渲染

          每一類定制組件都有唯一的name名,每一個組件在node樹中被創(chuàng)建時也有唯一id,方便后期的編輯和渲染,

          遍歷node樹遞歸調(diào)用主渲染文件,根據(jù)組件name名和相應(yīng)數(shù)據(jù),渲染出對應(yīng)組件

          4.移動端適配和預(yù)覽

          • 由于移動端和PC端樣式和差異較大,就沒考慮一套代碼自適應(yīng),每個定制組件對應(yīng)兩個文件PC和h5,渲染展示時,判斷當(dāng)前平臺進(jìn)而作出相應(yīng)的展示
          • h5預(yù)覽使用iframe,h5預(yù)覽單獨(dú)占一個路由,賦值給iframe的src屬性

          5.文字快速編輯

          活動頁面上會涉及很多文字,用戶想修改,有幾種方法

          1. 編輯按鈕,把它變成輸入框,完成后,保存按鈕,

          2. 在屬性欄放輸入框做關(guān)系映射,

          以上兩種可能都不太直觀,也比較麻煩

          就想到了使用contenteditable屬性,給標(biāo)簽加上后,可直接修改文字,可設(shè)置雙擊修改,延時保存,并設(shè)置防抖,大多數(shù)組件都會存在此需求,直接標(biāo)簽綁定事件比較麻煩,因此設(shè)置了全局綁定事件監(jiān)聽,控制注冊和及時銷毀

          請看下圖演示

          特點(diǎn)

          編輯回退和取消回退

          每一次操作后,都存儲一下node樹,并放入回退隊列,,通過指向隊列的上一個或下一個來實(shí)現(xiàn)回退和取消回退,通過并限制隊列長度,控制瀏覽器內(nèi)存使用

          組件上下移動和指向父組件功能

          • 用戶編輯時,可能會對組件的位置進(jìn)行調(diào)整,還有組件嵌套層級關(guān)系過多時,可能選中當(dāng)前組件的父組件比較困難,基于此提供了這兩個功能,

          • 具體實(shí)現(xiàn),就是通過組件的唯一Id,遍歷node樹查找,刪除當(dāng)前組件,然后插入在兄弟節(jié)點(diǎn)的上面或下面,

          思考和優(yōu)化

          1. 關(guān)于活動頁保存展現(xiàn)的心路歷程:單獨(dú)開一個項目,或項目單獨(dú)開一個頁面,作為活動展示使用,根據(jù)唯一id,獲取不同數(shù)據(jù)渲染配置頁面

          問題:

          • 代碼不存粹,代碼量較大,包含了所有定制組件模版

          • 項目出現(xiàn)問題影響所有頁面

          • 項目或組件出現(xiàn)改動,要考慮對在線活動的影響

          所以此想法被PASS,每創(chuàng)建保存一個活動頁,都會在服務(wù)器固化的生成唯一的html文件和靜態(tài)資源,保證不被影響

          1. 優(yōu)化想法:直接把編輯好的活動頁面html片段傳給后端,后端直接生成渲染好的活動頁面,

          優(yōu)點(diǎn):

          • 訪問頁面時不用再根據(jù)node樹臨時渲染,頁面加載效率提高,

          • 代碼量減少

          總結(jié)

          總體是滿足了產(chǎn)品需求,同時從三方面考慮

          1. 提高運(yùn)營人員搭建頁面工作效率, 增強(qiáng)產(chǎn)品可用性

          2. 降低開發(fā)人員編寫定制組件難度和上手難度, 提高項目可維護(hù)性與可拓展性

          3. 優(yōu)化用戶體驗(yàn),增強(qiáng)頁面加載效率,(其他方面比如:可讀性,可觀賞性,可操作性)

          點(diǎn)個『在看』支持下?

          瀏覽 97
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  插鸡巴网站一区二区三区豆花视频 | 无码一区.一区 | 午夜性爱视频 | 偷偷操网站 | 国产一级无码Av片在线观看 |