Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 一区二区高清在线,五月狠狠亚洲小说专区,韩剧网免费韩国电视剧在线观看

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          HTML表單元素初識(shí)2-零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          單元素屬性2

          為了不再被疑似涉嫌低俗,我只能給大家提供這種圖片了,耐得住寂寞才能學(xué)有所成!吸引人的東西未必珍貴。

          昨天我們學(xué)習(xí)了《HTML表單元素初識(shí)1——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作》(目錄在結(jié)尾),大家通過(guò)學(xué)習(xí)對(duì)HTML頁(yè)面中的表單元素的基本寫(xiě)法已經(jīng)非常熟悉了。同時(shí)也學(xué)會(huì)了通過(guò)變換<input/>標(biāo)簽中的type屬性的值為表單賦予不同的功能,例如輸入文本和建立多選表單。期間,對(duì)name與value這兩個(gè)屬性的作用與特點(diǎn)進(jìn)行了闡釋。今天我們繼續(xù)學(xué)習(xí)新的表單內(nèi)容。

          建立單選表單:單選表單把<input/>標(biāo)簽的type屬性修改為"radio"(收音機(jī)),為什么單選表單使用"收音機(jī)"這個(gè)詞呢?其實(shí)道理很簡(jiǎn)單,收音機(jī)調(diào)頻旋鈕是對(duì)應(yīng)角度對(duì)應(yīng)相應(yīng)調(diào)頻,不可能一個(gè)角度對(duì)應(yīng)兩個(gè)調(diào)頻,所以選這個(gè)就不能選其他的表單中的type屬性使用"radio"這個(gè)詞表示,是不是很形象。

          示例代碼如下:

          <form>
            最高學(xué)歷:<br>
            <input type = "radio" name = "education" value = "highSchool"/>高中
            <input type = "radio" name = "education" value = "bachelor"/>本科
            <input type = "radio" name = "education" value = "master"/>碩士
            <input type = "radio" name = "education" value = "doctor"/>博士
            <br>
            <input type = "submit" value = "submit"/>
          </form>

          因?yàn)槊鑼?xiě)的是最高學(xué)歷,一般人最高學(xué)歷只有一個(gè),不可能又是學(xué)士又是博士,因此使用單選表單。

          向服務(wù)器提交時(shí),name叫做"education"(教育),value對(duì)應(yīng)不同層次。

          頁(yè)面效果如下:

          大家可以點(diǎn)點(diǎn)試試,每次只能有一個(gè)被選中。如圖:

          綜合練習(xí):到這為止,我們把之前零散的代碼拼湊一下看看效果吧!

          <!DOCTYPE HTML>
            <html>
            <head> 
            <title>表單 </title>
            </head> 
            <body>
            <form>
            會(huì)員名稱:
            <input type = "text" placeholder = "請(qǐng)輸入英文或漢語(yǔ)拼音" name="memberName"/><br>
            會(huì)員密碼:
            <input type = "text" placeholder = "請(qǐng)輸入英文字母、特殊符號(hào)、數(shù)字" name="passWord"/><br>
            確認(rèn)密碼:
            <input type = "text" name="confirmPassWord"/><br>
            <input type = "submit" value = "提交"/><br>
            </form>
            <hr>
            <form>
            興趣愛(ài)好:
            <br>
            <input type = "checkbox" name = "hobby" value = "reading"/>讀書(shū)
            <input type = "checkbox" name = "hobby" value = "film"/>電影
            <input type = "checkbox" name = "hobby" value = "painting"/>繪畫(huà)
            <input type = "checkbox" name = "hobby" value = "music"/>音樂(lè)
            <br>
            最高學(xué)歷:<br>
            <input type = "radio" name = "education" value = "highSchool"/>高中
            <input type = "radio" name = "education" value = "bachelor"/>本科
            <input type = "radio" name = "education" value = "master"/>碩士
            <input type = "radio" name = "education" value = "doctor"/>博士
            <br>
            <input type = "submit" value = "submit"/>
            </form>
            </body>
            </html>

          頁(yè)面效果如下:

          密碼輸入:我們?cè)谑褂蒙鲜霰韱屋斎朊艽a時(shí)發(fā)現(xiàn),密碼時(shí)實(shí)時(shí)顯示在輸入框中,這一點(diǎn)是不安全的,如圖:

          如何讓密碼隱藏呢?其實(shí)看過(guò)上一篇中type屬性列表的小伙伴肯定猜到了,把type從"text"改為password。示例代碼如下:

          會(huì)員密碼:
          <input type = "password" placeholder = "請(qǐng)輸入英文字母、特殊符號(hào)、數(shù)字" name="passWord"/><br>
          確認(rèn)密碼:
          <input type = "password" name="confirmPassWord"/><br>

          頁(yè)面效果如下:

          上傳文件:使用type="file"可以上傳文件!

          示例代碼如下:寫(xiě)在"submit"的上面即可。

          <input type = "file"/><br><input type = "submit" value = "submit"/>

          頁(yè)面效果如下:

          點(diǎn)擊"瀏覽"看下效果:

          神奇!

          使用圖片制作按鈕:將type="image"即可,代碼如下:

          <input type = "image" src = "img/示例圖片/submit.jpg"/><br>

          頁(yè)面效果如下:用一個(gè)src導(dǎo)入圖片即可。

          示例圖片:路徑自行設(shè)置即可!

          為表單設(shè)置一個(gè)重置按鈕:如果用戶打算重置表單內(nèi)容后從新填寫(xiě),我們可以給他這樣一個(gè)按鈕:

          <input type="reset" /><br>

          頁(yè)面如圖所示:

          大家要注意的是這個(gè)"重置"按鈕的作用范圍僅僅是它所在的<form></form>標(biāo)簽之間!

          舉個(gè)例子,如圖:

          下面我們點(diǎn)擊"重置"后效果如下:

          form1中的內(nèi)容沒(méi)有被清空,"重置"按鈕所在的form2內(nèi)容被清空了!

          type屬性值講解我們到此結(jié)束了,hidden值這里先不給大家介紹,以后有機(jī)會(huì)再細(xì)說(shuō)。

          button值以后在JavaScript部分還會(huì)細(xì)說(shuō),這里也先略過(guò)。

          除了<input/>外,還有其他一些標(biāo)簽,例如<select>或<textarea>等有趣且實(shí)用的標(biāo)簽,我們明天再學(xué)習(xí)吧!

          今天的內(nèi)容先到這里。希望大家看完之后可以寫(xiě)寫(xiě)代碼進(jìn)行實(shí)操。代碼這種東西即使再簡(jiǎn)單,寫(xiě)過(guò)和沒(méi)寫(xiě)過(guò)的體會(huì)也是不一樣的。這個(gè)部分的代碼使用"文本編輯器"就可以實(shí)踐。具體操作請(qǐng)?jiān)斠?jiàn)《》。

          碎片化的知識(shí)其實(shí)對(duì)人并沒(méi)有多大作用,但是我們的時(shí)間在現(xiàn)代化的生活節(jié)奏中被撕地越來(lái)越碎,因此我希望大家可以利用碎片時(shí)間來(lái)學(xué)習(xí)完整的知識(shí),所以,以短篇的形式,每天20分鐘左右,通過(guò)積少成多的辦法為大家提供零基礎(chǔ)頁(yè)面制作的教程體系是我的主要目的。希望大家學(xué)有所成!

          喜歡我的教程的小伙伴請(qǐng)關(guān)注我,點(diǎn)贊也會(huì)讓我充滿動(dòng)力!

          喜歡的小伙伴請(qǐng)關(guān)注和轉(zhuǎn)發(fā),閱讀中遇到任何問(wèn)題請(qǐng)給我留言,如有疏漏或錯(cuò)誤歡迎大家斧正,不勝感激!

          HTML完整學(xué)習(xí)目錄

          HTML序章(學(xué)習(xí)目的、對(duì)象、基本概念)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML是什么?——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          第一個(gè)HTML頁(yè)面如何寫(xiě)?——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML頁(yè)面中head標(biāo)簽有啥用?——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          初識(shí)meta標(biāo)簽與SEO——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML中的元素使用方法1——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML中的元素使用方法2——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML元素中的屬性1——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML元素中的屬性2(路徑詳解)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          使用HTML添加表格1(基本元素)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          使用HTML添加表格2(表格頭部與腳部)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          使用HTML添加表格3(間距與顏色)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          使用HTML添加表格4(行顏色與表格嵌套)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          16進(jìn)制顏色表示與RGB色彩模型——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML中的塊級(jí)元素與內(nèi)聯(lián)元素——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          初識(shí)HTML中的<div>塊元素——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          在HTML頁(yè)面中嵌入其他頁(yè)面的方法——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          封閉在家學(xué)網(wǎng)頁(yè)制作!為頁(yè)面嵌入PDF文件——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML表單元素初識(shí)1——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML表單元素初識(shí)2——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML表單3(下拉列表、多行文字輸入)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML表單4(form的action、method屬性)——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML列表制作講解——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          為HTML頁(yè)面添加視頻、音頻的方法——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          音視頻格式轉(zhuǎn)換神器與html視頻元素加字幕——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          HTML中使用<a>標(biāo)簽實(shí)現(xiàn)文本內(nèi)鏈接——零基礎(chǔ)自學(xué)網(wǎng)頁(yè)制作

          為一名技術(shù)工作者, 我們經(jīng)常會(huì)遇到編寫(xiě)技術(shù)文檔, 技術(shù)分享等需求, 網(wǎng)上也有很多現(xiàn)成的文檔管理工具, 出于好奇心, 我拉著朋友一起實(shí)現(xiàn)了一個(gè), 用來(lái)自給自足. 接下來(lái)就來(lái)介紹一下輕量級(jí)且靈活方便的文檔編輯工具—— powerNice.

          powerNice 提供兩種方式來(lái)編寫(xiě)文章/文檔, 即程序員最喜歡的 markdown, 也可以使用非技術(shù)人員最容易上手的富文本編輯器.

          demo演示



          技術(shù)選型

          實(shí)現(xiàn) powerNice 在線文檔編輯器我們采用如下核心技術(shù)棧: - React - Ant Design - Dva - For-editor - Braft-editor - Nodejs - 瀏覽器指紋識(shí)別技術(shù)

          功能盤(pán)點(diǎn)

          1.多模式編輯

          多模式編輯主要是指我們可以用富文本和md編輯器來(lái)編輯我們的文章, 我們采用最熟悉的 React 來(lái)實(shí)現(xiàn), 效果如下:



          2. 多主題
          目前支持2套主題, 淺色和深色, 主要從用戶體驗(yàn)的角度方便用戶夜間寫(xiě)作. 效果如下: 1. 深色

          2. 淺色


          3. 支持一鍵導(dǎo)入導(dǎo)出
          為了提高我們的寫(xiě)作效率以及對(duì)文章的多路復(fù)用, 我們提供了一鍵導(dǎo)入導(dǎo)出文件等功能, 具體如下:

          • 導(dǎo)出 Markdown
          • 導(dǎo)出 PDF
          • 基于文章導(dǎo)出海報(bào)圖
          • 導(dǎo)入 Markdown 文件
          • 下載文章 html 內(nèi)容

          使用截圖如下:


          下載的html內(nèi)容預(yù)覽如下:

          還原度還是非常高的~
          4. 多模式預(yù)覽
          多模式預(yù)覽主要是右側(cè)的預(yù)覽區(qū), 我們支持手機(jī)端預(yù)覽和
          pc端預(yù)覽, 如下圖:


          5. 字?jǐn)?shù)行數(shù)統(tǒng)計(jì)
          字?jǐn)?shù)行數(shù)統(tǒng)計(jì)主要是幫助作者做內(nèi)容統(tǒng)計(jì), 這塊實(shí)現(xiàn)不是很難, 我們看看預(yù)覽效果:


          6. 文章管理
          文章管理主要是管理用戶編寫(xiě)的內(nèi)容, 這里因?yàn)槲覀冏龅氖蔷€上工具, 用戶識(shí)別主要采用瀏覽器指紋識(shí)別技術(shù)來(lái)區(qū)分用戶, 用戶可以輕松在編輯器文章列表中切換文章進(jìn)行編輯, 效果如下:


          核心技術(shù)實(shí)現(xiàn)
          1. 導(dǎo)入導(dǎo)出多類型文件

          • 導(dǎo)入md/html文件 導(dǎo)入md文件我們主要利用antupload組件和FileReader API, 具體實(shí)現(xiàn)如下:

          { name: 'file', showUploadList: false, beforeUpload(file: any): any { const reader = new FileReader() reader.onload = function(e: Event) { const data = (e as any).target.result if (editor === 'richText') { // ... } else { // ... } } reader.readAsText(file) }, }

          • 下載html 下載html的原理也很簡(jiǎn)單, 我們拿到渲染后的html字符串, 利用html模版將其包裝成完整的html, 最后再存儲(chǔ)為File對(duì)象, 利用file-saver實(shí)現(xiàn)下載. 思路如下:


          核心代碼如下:

           const doc = document.querySelector('.for-markdown-preview') as HTMLElement const html = createMDHtml(doc.innerHTML, article) file = new File([html], `${moment().format('YYYYMMDDHHmmss')}.html`, { type: 'text/html;charset=utf-8' }) // 下載文件 saveAs(file)


          2. 基于瀏覽器指紋識(shí)別技術(shù)的用戶識(shí)別
          瀏覽器指紋這塊知識(shí)點(diǎn)涉及的比較多, 筆者這里簡(jiǎn)單介紹一下
          canvas指紋.
          Canvas指紋是利用系統(tǒng)之間, 字體渲染引擎, 抗鋸齒、次像素渲染等處理方式的差異而實(shí)現(xiàn)的一種指紋識(shí)別技術(shù). 我們使用canvas將同樣的文字轉(zhuǎn)成圖片, 即便使用Canvas繪制相同的元素,但由于上述的差別得到的結(jié)果也是不同的。

          所以我們可以利用以上技術(shù), 對(duì)不同用戶瀏覽器進(jìn)行識(shí)別, 從而區(qū)分用戶(雖然存在概率事件), 實(shí)現(xiàn)無(wú)需登錄就能保存對(duì)應(yīng)內(nèi)容的目的. 基本實(shí)現(xiàn)代碼如下:

          createFingerprint = () => {   const canvas = document.getElementById('anchor-uuid') as HTMLCanvasElement   const context = canvas.getContext('2d') as CanvasRenderingContext2D   context.font = '18pt Arial'   context.textBaseline = 'top'   context.fillText('hello, user.', 2, 2)   const fingerprint = canvas.toDataURL('image/jpeg')    // hash   const secret = 'nice'   const hash = crypto.createHmac('sha256', secret)     .update(fingerprint)     .digest('hex')    return hash }


          大家也可以參考此方法來(lái)設(shè)計(jì)自己的指紋識(shí)別方案.
          在線體驗(yàn): 傳送門(mén)
          最后
          目前筆者也在持續(xù)更新
          H5編輯器 H5-Dooring, 近期更新如下:

          • 修復(fù)圖片庫(kù)選擇bug
          • 添加省市級(jí)聯(lián)組件
          • 添加批量導(dǎo)入 excel 數(shù)據(jù)的能力
          • 添加表單自定義校驗(yàn)
          • 音頻組件添加自動(dòng)播放控制, 循環(huán)播放等配置項(xiàng)
          • 添加橫向滑動(dòng)組件

          覺(jué)得有用 ?喜歡就收藏,在 “趣談前端”,發(fā)現(xiàn)更多有趣的H5游戲, webpack,node,gulp,css3,javascript,nodeJS,canvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn).

          想必你一定使用過(guò)易企秀或百度H5等微場(chǎng)景生成工具制作過(guò)炫酷的h5頁(yè)面,除了感嘆其神奇之處有沒(méi)有想過(guò)其實(shí)現(xiàn)方式呢?本文從零開(kāi)始實(shí)現(xiàn)一個(gè)H5編輯器項(xiàng)目完整設(shè)計(jì)思路和主要實(shí)現(xiàn)步驟,并開(kāi)源前后端代碼。有需要的小伙伴可以按照該教程從零實(shí)現(xiàn)自己的H5編輯器。(實(shí)現(xiàn)起來(lái)并不復(fù)雜,該教程只是提供思路,并非最佳實(shí)踐)

          Github: https://github.com/huangwei9527/quark-h5

          演示地址:http://47.104.247.183:4000/

          演示帳號(hào)密碼均admin

          編輯器預(yù)覽:



          技術(shù)棧

          前端: vue: 模塊化開(kāi)發(fā)少不了angular,react,vue三選一,這里選擇了vue。 vuex: 狀態(tài)管理 sass: css預(yù)編譯器。 element-ui:不造輪子,有現(xiàn)成的優(yōu)秀的vue組件庫(kù)當(dāng)然要用起來(lái)。沒(méi)有的自己再封裝一些就可以了。 loadsh:工具類

          服務(wù)端: koa:后端語(yǔ)言采用nodejs,koa文檔和學(xué)習(xí)資料也比較多,express原班人馬打造,這個(gè)正合適。 mongodb:一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫(kù),比較靈活。

          閱讀前準(zhǔn)備

          1、了解vue技術(shù)棧開(kāi)發(fā) 2、了解koa 3、了解mongodb

          工程搭建

          基于vue-cli3環(huán)境搭建

          • 如何規(guī)劃好我們項(xiàng)目的目錄結(jié)構(gòu)?首先我們需要有一個(gè)目錄作為前端項(xiàng)目,一個(gè)目錄作為后端項(xiàng)目。所以我們要對(duì)vue-cli 生成的項(xiàng)目結(jié)構(gòu)做一下改造:
          ···
          ·
          |-- client				// 原 src 目錄,改成 client 用作前端項(xiàng)目目錄
          |-- server				// 新增 server 用于服務(wù)端項(xiàng)目目錄
          |-- engine-template		// 新增 engine-template 用于頁(yè)面模板庫(kù)目錄
          |-- docs				// 新增 docs 預(yù)留編寫(xiě)項(xiàng)目文檔目錄
          ·
          ···
          復(fù)制代碼
          • 這樣的話 我們需要再把我們webpack配置文件稍作一下調(diào)整,首先是把原先的編譯指向src的目錄改成client,其次為了 npm run build 能正常編譯 client 我們也需要為 babel-loader 再增加一個(gè)編譯目錄: 根目錄新增vue.config.js,目的是為了改造項(xiàng)目入口,改為:client/main.js module.exports = { pages: { index: { entry: "client/main.js" } } } 復(fù)制代碼 babel-loader能正常編譯 client, engine-template目錄, 在vue.config.js新增如下配置 // 擴(kuò)展 webpack 配置 chainWebpack: config => { config.module .rule('js') .include.add(/engine-template/).end() .include.add(/client/).end() .use('babel') .loader('babel-loader') .tap(options => { // 修改它的選項(xiàng)... return options }) } 復(fù)制代碼

          這樣我們搭建起來(lái)一個(gè)簡(jiǎn)易的項(xiàng)目目錄結(jié)構(gòu)。

          工程目錄結(jié)構(gòu)

          |-- client					--------前端項(xiàng)目界面代碼
              |--common					--------前端界面對(duì)應(yīng)靜態(tài)資源
              |--components				--------組件
              |--config					--------配置文件
              |--eventBus					--------eventBus
              |--filter					--------過(guò)濾器
              |--mixins					--------混入
              |--pages					--------頁(yè)面
              |--router					--------路由配置
              |--store					--------vuex狀態(tài)管理
              |--service					--------axios封裝
              |--App.vue					--------App
              |--main.js					--------入口文件
              |--permission.js			--------權(quán)限控制
          |-- server					--------服務(wù)器端項(xiàng)目代碼
              |--confog					--------數(shù)據(jù)庫(kù)鏈接相關(guān)
              |--middleware				--------中間件
              |--models					--------Schema和Model
              |--routes					--------路由
              |--views					--------ejs頁(yè)面模板
              |--public					--------靜態(tài)資源
              |--utils					--------工具方法
              |--app.js					--------服務(wù)端入口
          |-- common					--------前后端公用代碼模塊(如加解密)
          |-- engine-template			--------頁(yè)面模板引擎,使用webpack打包成js提供頁(yè)面引用
          |-- docs					--------預(yù)留編寫(xiě)項(xiàng)目文檔目錄
          |-- config.json				--------配置文件
          復(fù)制代碼

          前端編輯器實(shí)現(xiàn)

          編輯器的實(shí)現(xiàn)思路是:編輯器生成頁(yè)面JSON數(shù)據(jù),服務(wù)端負(fù)責(zé)存取JSON數(shù)據(jù),渲染時(shí)從服務(wù)端取數(shù)據(jù)JSON交給前端模板處理。



          數(shù)據(jù)結(jié)構(gòu)

          確認(rèn)了實(shí)現(xiàn)邏輯,數(shù)據(jù)結(jié)構(gòu)也是非常重要的,把一個(gè)頁(yè)面定義成一個(gè)JSON數(shù)據(jù),數(shù)據(jù)結(jié)構(gòu)大致是這樣的:

          頁(yè)面工程數(shù)據(jù)接口

          {
          	title: '', // 標(biāo)題
          	description: '', //描述
          	coverImage: '', // 封面
          	auther: '', // 作者
          	script: '', // 頁(yè)面插入腳本
          	width: 375, // 高
          	height: 644, // 寬
          	pages: [], // 多頁(yè)頁(yè)面
          	shareConfig: {}, // 微信分享配置
          	pageMode: 0, // 渲染模式,用于擴(kuò)展多種模式渲染,翻頁(yè)h5/長(zhǎng)頁(yè)/PC頁(yè)面等等
          }
          復(fù)制代碼

          多頁(yè)頁(yè)面pages其中一頁(yè)數(shù)據(jù)結(jié)構(gòu):

          {
          	name: '',
          	elements: [], // 頁(yè)面元素
          	commonStyle: {
          		backgroundColor: '',
          		backgroundImage: '',
          		backgroundSize: 'cover'
          	},
          	config: {}
          }
          復(fù)制代碼

          元素?cái)?shù)據(jù)結(jié)構(gòu):

          {
          	elName: '', // 組件名
          	animations: [], // 圖層的動(dòng)畫(huà),可以支持多個(gè)動(dòng)畫(huà)
          	commonStyle: {}, // 公共樣式,默認(rèn)樣式
          	events: [], // 事件配置數(shù)據(jù),每個(gè)圖層可以添加多個(gè)事件
          	propsValue: {}, // 屬性參數(shù)
          	value: '', // 綁定值
          	valueType: 'String', // 值類型
          	isForm: false // 是否是表單控件,用于表單提交時(shí)獲取表單數(shù)據(jù)
          }
          復(fù)制代碼

          編輯器整體設(shè)計(jì)

          • 一個(gè)組件選擇區(qū),提供使用者選擇需要的組件
          • 一個(gè)編輯預(yù)覽畫(huà)板,提供使用者拖拽排序頁(yè)面預(yù)覽的功能
          • 一個(gè)組件屬性編輯,提供給使用者編輯組件內(nèi)部props、公共樣式和動(dòng)畫(huà)的功能 如圖:


          用戶在左側(cè)組件區(qū)域選擇組件添加到頁(yè)面上,編輯區(qū)域通過(guò)動(dòng)態(tài)組件特性渲染出每個(gè)元素組件。


          最后,點(diǎn)擊保存將頁(yè)面數(shù)據(jù)提交到數(shù)據(jù)庫(kù)。至于數(shù)據(jù)怎么轉(zhuǎn)成靜態(tài) HTML方法有很多。還有頁(yè)面數(shù)據(jù)我們?nèi)慷加校覀兛梢宰鲰?yè)面的預(yù)渲染,骨架屏,ssr,編譯時(shí)優(yōu)化等等。而且我們也可以對(duì)產(chǎn)出的活動(dòng)頁(yè)做數(shù)據(jù)分析~有很多想象的空間。

          核心代碼

          編輯器核心代碼,基于 Vue 動(dòng)態(tài)組件特性實(shí)現(xiàn):



          為大家附上 Vue 官方文檔:cn.vuejs.org/v2/api/#is

          畫(huà)板元素渲染

          編輯畫(huà)板只需要循環(huán)遍歷pages[i].elements數(shù)組,將里面的元素組件JSON數(shù)據(jù)取出,通過(guò)動(dòng)態(tài)組件渲染出各個(gè)組件,支持拖拽改變位置尺寸.

          元素組件管理

          在client目錄新建plugins來(lái)管理組件庫(kù)。也可以將該組件庫(kù)發(fā)到npm上工程中通過(guò)npm管理

          組件庫(kù)

          編寫(xiě)組件,考慮的是組件庫(kù),所以我們竟可能讓我們的組件支持全局引入和按需引入,如果全局引入,那么所有的組件需要要注冊(cè)到Vue component 上,并導(dǎo)出:

          client/plugins下新建index.js入口文件

          ```
          /**
           * 組件庫(kù)入口
           * */
          import Text from './text'
          // 所有組件列表
          const components = [
          	Text
          ]
          // 定義 install 方法,接收 Vue 作為參數(shù)
          const install = function (Vue) {
          	// 判斷是否安裝,安裝過(guò)就不繼續(xù)往下執(zhí)行
          	if (install.installed) return
          	install.installed = true
          	// 遍歷注冊(cè)所有組件
          	components.map(component => Vue.component(component.name, component))
          }
          
          // 檢測(cè)到 Vue 才執(zhí)行,畢竟我們是基于 Vue 的
          if (typeof window !== 'undefined' && window.Vue) {
          	install(window.Vue)
          }
          
          export default {
          	install,
          	// 所有組件,必須具有 install,才能使用 Vue.use()
          	Text
          }
          ```
          復(fù)制代碼

          組件開(kāi)發(fā)

          示例: text文本組件

          client/plugins下新建text組件目錄

          |-- text                --------text組件
              |--src              --------資源
              	|--index.vue    --------組件
              |--index.js         --------入口
          復(fù)制代碼

          text/index.js

          // 為組件提供 install 方法,供組件對(duì)外按需引入
          import Component from './src/index'
          Component.install = Vue => {
          	Vue.component(Component.name, Component)
          }
          export default Component
          復(fù)制代碼

          text/src/index.vue

          <!--text.vue-->
          <template>
            <div class="qk-text">
              {{text}}
            </div>
          </template>
          
          <script>
          	export default {
          		name: 'QkText', // 這個(gè)名字很重要,它就是未來(lái)的標(biāo)簽名<qk-text></qk-text>
          		props: {
          			text: {
          				type: String,
          				default: '這是一段文字'
                		}
          		}
          	}
          </script>
          
          <style lang="scss" scoped>
          </style>
          復(fù)制代碼

          編輯器里使用組件庫(kù):

          // 引入組件庫(kù)
          import QKUI from 'client/plugins/index'
          // 注冊(cè)組件庫(kù)
          Vue.use(QKUI)
          
          // 使用:
          <qk-text text="這是一段文字"></qk-text>
          復(fù)制代碼

          按照這個(gè)組件開(kāi)發(fā)方式我們可以擴(kuò)展任意多的組件,來(lái)豐富組件庫(kù)

          需要注意的是這里的組件最外層寬高都要求是100%

          配置文件

          Quark-h5編輯器左側(cè)選擇組件區(qū)域可以通過(guò)一個(gè)配置文件定義可選組件 新建一個(gè)ele-config.js配置文件:

          export default [
          	{
          		title: '基礎(chǔ)組件',
          		components: [
          			{
          				elName: 'qk-text', // 組件名,與組件庫(kù)名稱一致
          				title: '文字',
          				icon: 'iconfont iconwenben',
          				// 給每個(gè)組件配置默認(rèn)顯示樣式
          				defaultStyle: {
          					height: 40
          				}
          			}
          		]
          	},
          	{
          		title: '表單組件',
          		components: []
          	},
          	{
          		title: '功能組件',
          		components: []
          	},
          	{
          		title: '業(yè)務(wù)組件',
          		components: []
          	}
          ]
          復(fù)制代碼

          公共方法中提供一個(gè)function 通過(guò)組件名和默認(rèn)樣式獲取元素組件JSON,getElementConfigJson(elName, defaultStyle)方法

          元素屬性編輯

          公共屬性樣式編輯

          公共樣式屬性編輯比較簡(jiǎn)單就是對(duì)元素JSON對(duì)象commonStyles字段進(jìn)行編輯操作

          props屬性編輯

          1.為組件的每一個(gè)prop屬性開(kāi)發(fā)一個(gè)屬性編輯組件. 例如:QkText組件需要text屬性,新增一個(gè)attr-qk-text組件來(lái)操作該屬性 2.獲取組件prop對(duì)象 3.遍歷prop對(duì)象key, 通過(guò)key判斷顯示哪些屬性編輯組件

          元素添加動(dòng)畫(huà)實(shí)現(xiàn)

          動(dòng)畫(huà)效果引入Animate.css動(dòng)畫(huà)庫(kù)。元素組件動(dòng)畫(huà),可以支持多個(gè)動(dòng)畫(huà)。數(shù)據(jù)存在元素JSON對(duì)象animations數(shù)組里。

          選擇面板hover預(yù)覽動(dòng)畫(huà)


          監(jiān)聽(tīng)mouseover和mouseleave,當(dāng)鼠標(biāo)移入時(shí)將動(dòng)畫(huà)className添加入到元素上,鼠標(biāo)移出時(shí)去掉動(dòng)畫(huà)lassName。這樣就實(shí)現(xiàn)了hover預(yù)覽動(dòng)畫(huà)


          編輯預(yù)覽動(dòng)畫(huà)

          組件編輯時(shí)支持動(dòng)畫(huà)預(yù)覽和單個(gè)動(dòng)畫(huà)預(yù)覽。

          封裝一個(gè)動(dòng)畫(huà)執(zhí)行方法


          /**
           * 動(dòng)畫(huà)方法, 將動(dòng)畫(huà)css加入到元素上,返回promise提供執(zhí)行后續(xù)操作(將動(dòng)畫(huà)重置)
           * @param $el 當(dāng)前被執(zhí)行動(dòng)畫(huà)的元素
           * @param animationList 動(dòng)畫(huà)列表
           * @param isDebugger 動(dòng)畫(huà)列表
           * @returns {Promise<void>}
           */
          export default async function runAnimation($el, animationList = [], isDebug , callback){
          	let playFn = function (animation) {
          		return new Promise(resolve => {
          			$el.style.animationName =  animation.type
          			$el.style.animationDuration =  `${animation.duration}s`
          			// 如果是循環(huán)播放就將循環(huán)次數(shù)置為1,這樣有效避免編輯時(shí)因?yàn)轭A(yù)覽循環(huán)播放組件播放動(dòng)畫(huà)無(wú)法觸發(fā)animationend來(lái)暫停組件動(dòng)畫(huà)
          			$el.style.animationIterationCount =  animation.infinite ? (isDebug ? 1 : 'infinite') : animation.interationCount
          			$el.style.animationDelay =  `${animation.delay}s`
          			$el.style.animationFillMode =  'both'
          			let resolveFn = function(){
          				$el.removeEventListener('animationend', resolveFn, false);
          				$el.addEventListener('animationcancel', resolveFn, false);
          				resolve()
          			}
          			$el.addEventListener('animationend', resolveFn, false)
          			$el.addEventListener('animationcancel', resolveFn, false);
          		})
          	}
          	for(let i = 0, len = animationList.length; i < len; i++){
          		await playFn(animationList[i])
          	}
          	if(callback){
          		callback()
          	}
          }
          復(fù)制代碼

          animationIterationCount 如果是編輯模式的化動(dòng)畫(huà)只執(zhí)行一次,不然無(wú)法監(jiān)聽(tīng)到動(dòng)畫(huà)結(jié)束animationend事件

          執(zhí)行動(dòng)畫(huà)前先將元素樣式style緩存起來(lái),當(dāng)動(dòng)畫(huà)執(zhí)行完再將原樣式賦值給元素

          let cssText = this.$el.style.cssText;
          runAnimations(this.$el, animations, true, () => {
          	this.$el.style.cssText = cssText
          })
          復(fù)制代碼

          元素添加事件

          提供事件mixins混入到組件,每個(gè)事件方法返回promise,元素被點(diǎn)擊時(shí)按順序執(zhí)行事件方法

          頁(yè)面插入js腳本

          參考百度H5,將腳本以script標(biāo)簽形式嵌入。頁(yè)面加載后執(zhí)行。 這里也可以考慮mixins方式混入到頁(yè)面或者組件,可根據(jù)業(yè)務(wù)需求自行擴(kuò)展,都是可以實(shí)現(xiàn)的。

          redo/undo歷史操作紀(jì)錄

          1. 歷史操作紀(jì)錄存在狀態(tài)機(jī)store.state.editor.historyCache數(shù)組中。
          2. 每次修改編輯操作都把整個(gè)pageDataJson字段push到historyCache
          3. 點(diǎn)擊redo/undo時(shí)根據(jù)index獲取到pageDataJson重新渲染頁(yè)面

          psd設(shè)計(jì)圖導(dǎo)入生成h5頁(yè)面

          將psd每個(gè)設(shè)計(jì)圖中的每個(gè)圖層導(dǎo)出成圖片保存到靜態(tài)資源服務(wù)器中,

          服務(wù)端安裝psd依賴

          cnpm install psd --save
          復(fù)制代碼

          加入psd.js依賴,并且提供接口來(lái)處理數(shù)據(jù)

          var PSD = require('psd');
          router.post('/psdPpload',async ctx=>{
          	const file = ctx.request.files.file; // 獲取上傳文件
          	let psd = await PSD.open(file.path)
          	var timeStr = + new Date();
          	let descendantsList = psd.tree().descendants();
          	descendantsList.reverse();
          	let psdSourceList = []
          	let currentPathDir = `public/upload_static/psd_image/${timeStr}`
          	for (var i = 0; i < descendantsList.length; i++){
          		if (descendantsList[i].isGroup()) continue;
          		if (!descendantsList[i].visible) continue;
          		try{
          			await descendantsList[i].saveAsPng(path.join(ctx.state.SERVER_PATH, currentPathDir + `/${i}.png`))
          			psdSourceList.push({
          				...descendantsList[i].export(),
          				type: 'picture',
          				imageSrc: ctx.state.BASE_URL + `/upload_static/psd_image/${timeStr}/${i}.png`,
          			})
          		}catch (e) {
          			// 轉(zhuǎn)換不出來(lái)的圖層先忽略
          			continue;
          		}
          	}
          	ctx.body = {
          		elements: psdSourceList,
          		document: psd.tree().export().document
          	};
          })
          復(fù)制代碼

          最后把獲取的數(shù)據(jù)轉(zhuǎn)義并返回給前端,前端獲取到數(shù)據(jù)后使用系統(tǒng)統(tǒng)一方法,遍歷添加統(tǒng)一圖片組件

          • psd源文件大小最好不要超過(guò)30M,過(guò)大會(huì)導(dǎo)致瀏覽器卡頓甚至卡死
          • 盡可能合并圖層,并柵格化所有圖層
          • 較復(fù)雜的圖層樣式,如濾鏡、圖層樣式等無(wú)法讀取

          html2canvas生成縮略圖

          這里只需要注意下圖片跨域問(wèn)題,官方提供html2canvas: proxy解決方案。它將圖片轉(zhuǎn)化為base64格式,結(jié)合使用設(shè)置(proxy: theProxyURL), 繪制到跨域圖片時(shí),會(huì)去訪問(wèn)theProxyURL下轉(zhuǎn)化好格式的圖片,由此解決了畫(huà)布污染問(wèn)題。 提供一個(gè)跨域接口

          /**
           * html2canvas 跨域接口設(shè)置
           */
          router.get('/html2canvas/corsproxy', async ctx => {
          	ctx.body =  await request(ctx.query.url)
          })
          復(fù)制代碼

          渲染模板

          實(shí)現(xiàn)邏輯

          在engine-template目錄下新建swiper-h5-engine頁(yè)面組件,這個(gè)組件接收到頁(yè)面JSON數(shù)據(jù)就可以把頁(yè)面渲染出來(lái)。跟編輯預(yù)覽畫(huà)板實(shí)現(xiàn)邏輯差不多。

          然后使用vue-cli庫(kù)打包命令將組件打包成engine.js庫(kù)文件。ejs模板引入該頁(yè)面組件配合json數(shù)據(jù)渲染出頁(yè)面



          適配方案

          提供兩種方案解決屏幕適配 1、等比例縮放 在將json元素轉(zhuǎn)換為dom元素的時(shí)候,對(duì)所有的px單位做比例轉(zhuǎn)換,轉(zhuǎn)換公式為 new = old * windows.x / pageJson.width,這里的pageJson.width是頁(yè)面的一個(gè)初始值,也是編輯時(shí)候的默認(rèn)寬度,同時(shí)viewport使用device-width。 2.全屏背景, 頁(yè)面垂直居中 因?yàn)闀?huì)存在上下或者左右有間隙的情況,這時(shí)候我們把背景顏色做全屏處理

          頁(yè)面垂直居中只適用于全屏h5, 以后擴(kuò)展長(zhǎng)頁(yè)和PC頁(yè)就不需要垂直居中處理。

          模板打包

          package.json中新增打包命令

          "lib:h5-swiper": "vue-cli-service build --target lib --name h5-swiper --dest server/public/engine_libs/h5-swiper engine-template/engine-h5-swiper/index.js"

          執(zhí)行npm run lib:h5-swiper 生成引擎模板js如圖



          頁(yè)面渲染

          ejs中引入模板

          <script src="/third-libs/swiper.min.js"></script>

          使用組件

          <engine-h5-swiper :pageData="pageData" />

          后端服務(wù)

          初始化項(xiàng)目

          工程目錄上文已給出,也可以使用 koa-generator 腳手架工具生成

          ejs-template 模板引擎配置

          app.js

          //配置ejs-template 模板引擎
          render(app, {
          	root: path.join(__dirname, 'views'),
          	layout: false,
          	viewExt: 'html',
          	cache: false,
          	debug: false
          });
          復(fù)制代碼

          koa-static靜態(tài)資源服務(wù)

          因?yàn)閔tml2canvas需要圖片允許跨域,所以在靜態(tài)資源服務(wù)中所有資源請(qǐng)求設(shè)置'Access-Control-Allow-Origin':'*'

          app.js

          //配置靜態(tài)web
          app.use(koaStatic(__dirname + '/public'), { gzip: true, setHeaders: function(res){
          	res.header( 'Access-Control-Allow-Origin', '*')
          }});
          復(fù)制代碼

          修改路由的注冊(cè)方式,通過(guò)遍歷routes文件夾讀取文件

          app.js

          const fs =  require('fs')
          fs.readdirSync('./routes').forEach(route=> {
              let api = require(`./routes/${route}`)
              app.use(api.routes(), api.allowedMethods())
          })
          復(fù)制代碼

          添加jwt認(rèn)證,同時(shí)過(guò)濾不需要認(rèn)證的路由,如獲取token

          app.js

          const jwt = require('koa-jwt')
          app.use(jwt({ secret: 'yourstr' }).unless({
              path: [
                  /^\/$/, /\/token/, /\/wechat/,
                  { url: /\/papers/, methods: ['GET'] }
              ]
          }));
          復(fù)制代碼

          中間件實(shí)現(xiàn)統(tǒng)一接口返回?cái)?shù)據(jù)格式,全局錯(cuò)誤捕獲并響應(yīng)

          middleware/formatresponse.js

          module.exports = async (ctx, next) => {
          	await next().then(() => {
          		if (ctx.status === 200) {
          			ctx.body = {
          				message: '成功',
          				code: 200,
          				body: ctx.body,
          				status: true
          			}
          		} else if (ctx.status === 201) { // 201處理模板引擎渲染
          
          		} else {
          			ctx.body = {
          				message: ctx.body || '接口異常,請(qǐng)重試',
          				code: ctx.status,
          				body: '接口請(qǐng)求失敗',
          				status: false
          			}
          		}
          	}).catch((err) => {
          		if (err.status === 401) {
          			ctx.status = 401;
          			ctx.body = {
          				code: 401,
          				status: false,
          				message: '登錄過(guò)期,請(qǐng)重新登錄'
          			}
          		} else {
          			throw err
          		}
          	})
          }
          
          復(fù)制代碼

          koa2-cors跨域處理

          當(dāng)接口發(fā)布到線上,前端通過(guò)ajax請(qǐng)求時(shí),會(huì)報(bào)跨域的錯(cuò)誤。koa2使用koa2-cors這個(gè)庫(kù)非常方便的實(shí)現(xiàn)了跨域配置,使用起來(lái)也很簡(jiǎn)單

          const cors = require('koa2-cors');
          app.use(cors());
          復(fù)制代碼

          連接數(shù)據(jù)庫(kù)

          我們使用mongodb數(shù)據(jù)庫(kù),在koa2中使用mongoose這個(gè)庫(kù)來(lái)管理整個(gè)數(shù)據(jù)庫(kù)的操作。

          • 創(chuàng)建配置文件

          根目錄下新建config文件夾,新建mongo.js

          // config/mongo.js
          const mongoose = require('mongoose').set('debug', true);
          const options = {
              autoReconnect: true
          }
          
          // username 數(shù)據(jù)庫(kù)用戶名
          // password 數(shù)據(jù)庫(kù)密碼
          // localhost 數(shù)據(jù)庫(kù)ip
          // dbname 數(shù)據(jù)庫(kù)名稱
          const url = 'mongodb://username:password@localhost:27017/dbname'
          
          module.exports = {
              connect: ()=> {            
                  mongoose.connect(url,options)
                  let db = mongoose.connection
                  db.on('error', console.error.bind(console, '連接錯(cuò)誤:'));
                  db.once('open', ()=> {
                      console.log('mongodb connect suucess');
                  })
              }
          }
          復(fù)制代碼

          把mongodb配置信息放到config.json中統(tǒng)一管理

          • 然后在app.js中引入
          const mongoConf = require('./config/mongo');
          mongoConf.connect();
          復(fù)制代碼

          ... 服務(wù)端具體接口實(shí)現(xiàn)就不詳細(xì)介紹了,就是對(duì)頁(yè)面的增刪改查,和用戶的登錄注冊(cè)難度不大

          啟動(dòng)運(yùn)行

          啟動(dòng)前端

          npm run dev-client
          復(fù)制代碼

          啟動(dòng)服務(wù)端

          npm run dev-server
          復(fù)制代碼

          注意: 如果沒(méi)有生成過(guò)引擎模板js文件的,需要先編輯引擎模板,否則預(yù)覽頁(yè)面加載頁(yè)面引擎.js 404報(bào)錯(cuò)

          編譯engine.js模板引擎

          npm run lib:h5-swiper

          主站蜘蛛池模板: 成人区人妻精品一区二区不卡网站| 蜜桃无码AV一区二区| 亚洲一区二区三区无码国产| 色欲AV蜜桃一区二区三| 人妻少妇一区二区三区| 婷婷亚洲综合一区二区| 骚片AV蜜桃精品一区| 精品一区二区三区在线观看l| 亚洲AV无码一区东京热久久 | 久久一区二区三区99| 天堂va在线高清一区| 51视频国产精品一区二区| 色欲精品国产一区二区三区AV| 亚洲午夜福利AV一区二区无码| 亚洲福利视频一区二区| 国产aⅴ一区二区三区| 国产激情一区二区三区在线观看 | 国产精品无码一区二区在线观一| 在线免费观看一区二区三区| 人妻视频一区二区三区免费| 精品少妇ay一区二区三区 | 美女一区二区三区| 日本不卡一区二区三区视频| 中文字幕一区二区免费| 亚洲乱码日产一区三区| 国产亚洲一区二区精品| 精品无码日韩一区二区三区不卡| 99精品一区二区三区| 国产高清在线精品一区| 亚洲乱色熟女一区二区三区蜜臀| 无码人妻精品一区二区三区久久久 | 中文字幕一区二区三区四区| 好爽毛片一区二区三区四| 成人日韩熟女高清视频一区| 亚洲av无码一区二区三区乱子伦| 91在线一区二区| 另类ts人妖一区二区三区| 爆乳熟妇一区二区三区霸乳 | 国产精品一区二区无线| 一区二区手机视频| 少妇激情av一区二区|