活動可視化搭建(拖拽生成頁面)
授權(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:?[],
??????}
??]
}
創(chuàng)建一個obj,編輯時 不操作dom,就是增刪改查obj數(shù)組,來更新視圖
保存時obj存在數(shù)據(jù)庫,在服務(wù)器某個地址生成html文件,靜態(tài)資源, obj通過模版?zhèn)鬟f掛載在window上,并生成唯一訪問路徑
發(fā)布時改變當(dāng)前活動頁面可訪問狀態(tài)
展示時,根據(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;
};
cur為當(dāng)前拖拽的組件,可通過其獲取鼠標(biāo)當(dāng)前坐標(biāo)box為當(dāng)前懸停區(qū)域,通過getBoundingClientRect方法獲取寬高及位置isDrop為當(dāng)前區(qū)域是否可放置,direction為當(dāng)前區(qū)域元素的排列方向,通過兩者設(shè)置,橫向或縱向的偏移量大小,假如當(dāng)前區(qū)域縱向排列,且可放置,則把可放置區(qū)域由上至下分成3份,1/4,1/2,1/2(具體偏移量可按照需求或用戶體驗(yàn)自由設(shè)置) 通過鼠標(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.文字快速編輯
活動頁面上會涉及很多文字,用戶想修改,有幾種方法
編輯按鈕,把它變成輸入框,完成后,保存按鈕,
在屬性欄放輸入框做關(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)化
關(guān)于活動頁保存展現(xiàn)的心路歷程:單獨(dú)開一個項目,或項目單獨(dú)開一個頁面,作為活動展示使用,根據(jù)唯一id,獲取不同數(shù)據(jù)渲染配置頁面
問題:
代碼不存粹,代碼量較大,包含了所有定制組件模版
項目出現(xiàn)問題影響所有頁面
項目或組件出現(xiàn)改動,要考慮對在線活動的影響
所以此想法被PASS,每創(chuàng)建保存一個活動頁,都會在服務(wù)器固化的生成唯一的html文件和靜態(tài)資源,保證不被影響
優(yōu)化想法:直接把編輯好的活動頁面html片段傳給后端,后端直接生成渲染好的活動頁面,
優(yōu)點(diǎn):
訪問頁面時不用再根據(jù)node樹臨時渲染,頁面加載效率提高,
代碼量減少
總結(jié)
總體是滿足了產(chǎn)品需求,同時從三方面考慮
提高運(yùn)營人員搭建頁面工作效率, 增強(qiáng)產(chǎn)品可用性
降低開發(fā)人員編寫定制組件難度和上手難度, 提高項目可維護(hù)性與可拓展性
優(yōu)化用戶體驗(yàn),增強(qiáng)頁面加載效率,(其他方面比如:可讀性,可觀賞性,可操作性)
點(diǎn)個『在看』支持下?
