<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登錄頁(yè)面</title>
/*總體的樣式*/
<style>
/*盒子樣式*/
#box{
width: 350px; //寬
height: 450px; //高
border: 1px solid black; //邊框
border-radius: 10px; //邊框弧度
font-family: 黑體; //字體
letter-spacing:8px; //段間距
word-spacing: 10px; //字間距
line-height: 40px; //行高
font-size: 18px; //字大小
padding: 20px; //內(nèi)邊框
}
/*給'注冊(cè)'賦予樣式*/
.register{
width:280px ; //寬
height: 50px; //高
background-color: skyblue; //背景顏色
border-radius: 10px; //邊框弧度
}
/*將所有邊框都改變*/
*{
border-radius: 5px; 邊框弧度
}
/*使用class選擇器,賦予number寬高和邊框*/
.number{
width: 185px; //寬
height: 27px; //高
border-width: 1px; //邊框?qū)挾?
}
/*id選擇器*/
#two{
width: 55px; //寬
border-width: 1px; 邊框?qū)挾? }
/*id選擇器*/
#phone{
width: 103px; //寬
}
/*class 選擇器*/
.boxs{
zoom: 75%; //清除浮動(dòng)
color: darkgray; //顏色
}
/*class選擇器*/
.box_a{
width: 50px; //寬
height: 50px; //高
background-image: url("../image/04.jpg "); //背景圖片
background-repeat: no-repeat; // 是否平鋪
background-size: 50px 25px; //背景尺寸
position: relative; //定位 相對(duì)定位
left: 310px; //定位后左移
bottom: 32px; //定位后下移
}
</style>
</head>
<body>
<div id="box">
<h1>請(qǐng)注冊(cè)</h1>
<p style="color: darkgray">已有帳號(hào)?<a href="https://im.qq.com/index">登錄</a></p>
<form action="" method="post">
<label for="name">用戶(hù)名</label>
<input type="text" placeholder="請(qǐng)輸入用戶(hù)名" id="name" class="number"> <br>
<label for="phone">手機(jī)號(hào)</label>
<select name="" id="two" class="number">
<optgroup>
<option style="" class="">+86</option>
</optgroup>
</select>
<input type="text" placeholder="請(qǐng)輸入手機(jī)號(hào)" id="phone" class="number"> <br>
<label for="mima">密?碼</label>
<input type="password" placeholder="請(qǐng)輸入密碼" id="mima" class="number"> <br>
<label for="mima">驗(yàn)證碼</label>
<input type="password" placeholder="請(qǐng)輸入驗(yàn)證碼" id="is" class="number">
<div class="box_a"></div>
<div class="boxs">
<input type="radio" id="" class="accept">閱讀并接受協(xié)議<br>
</div>
<input type="submit" value="注冊(cè)" class="register" >
</form>
</div>
</body>
</html>
在這里插入圖片描述
者開(kāi)源了一個(gè)Web思維導(dǎo)圖,在做導(dǎo)出為圖片的功能時(shí)走了挺多彎路,所以通過(guò)本文來(lái)記錄一下。
思維導(dǎo)圖的節(jié)點(diǎn)和連線都是通過(guò) svg渲染的,作為一個(gè)純 js 庫(kù),我們不考慮通過(guò)后端來(lái)實(shí)現(xiàn),所以只能思考如何通過(guò)純前端的方式來(lái)實(shí)現(xiàn)將svg或html轉(zhuǎn)換為圖片。
我們都知道 img 標(biāo)簽可以顯示 svg,然后 canvas 又可以渲染 img,那么是不是只要將svg渲染到img標(biāo)簽里,再通過(guò)canvas導(dǎo)出為圖片就可以呢,答案是肯定的。
const svgToPng = async (svgStr) => {
// 轉(zhuǎn)換成blob數(shù)據(jù)
let blob = new Blob([svgStr], {
type: 'image/svg+xml'
})
// 轉(zhuǎn)換成data:url數(shù)據(jù)
let svgUrl = await blobToUrl(blob)
// 繪制到canvas上
let imgData = await drawToCanvas(svgUrl)
// 下載
downloadFile(imgData, '圖片.png')
}
svgStr是要導(dǎo)出的svg字符串,比如:
然后通過(guò)Blob構(gòu)造函數(shù)創(chuàng)建一個(gè)類(lèi)型為image/svg+xml的blob數(shù)據(jù),接下來(lái)將blob數(shù)據(jù)轉(zhuǎn)換成data:URL:
const blobToUrl = (blob) => {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.onload = evt => {
resolve(evt.target.result)
}
reader.onerror = err => {
reject(err)
}
reader.readAsDataURL(blob)
})
}
其實(shí)就是base64格式的字符串。
接下來(lái)就可以通過(guò)img來(lái)加載,并渲染到canvas里進(jìn)行導(dǎo)出:
const drawToCanvas = (svgUrl) => {
return new Promise((resolve, reject) => {
const img = new Image()
// 跨域圖片需要添加這個(gè)屬性,否則畫(huà)布被污染了無(wú)法導(dǎo)出圖片
img.setAttribute('crossOrigin', 'anonymous')
img.onload = async () => {
try {
let canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height)
resolve(canvas.toDataURL())
} catch (error) {
reject(error)
}
}
img.onerror = e => {
reject(e)
}
img.src = svgUrl
})
}
canvas.toDataURL()方法返回的也是一個(gè)base64格式的data:URL字符串:
最后就可以通過(guò)a標(biāo)簽來(lái)下載:
const downloadFile = (file, fileName) => {
let a = document.createElement('a')
a.href = file
a.download = fileName
a.click()
}
實(shí)現(xiàn)很簡(jiǎn)單,效果也不錯(cuò),不過(guò)這樣就沒(méi)問(wèn)題了嗎,接下來(lái)我們插入兩張圖片試試。
第一張圖片是使用base64的data:URL方式插入的,第二張圖片是使用普通url插入的:
導(dǎo)出結(jié)果如下:
可以看到,第一張圖片沒(méi)有問(wèn)題,第二張圖片裂開(kāi)了,可能你覺(jué)得同源策略的問(wèn)題,但實(shí)際上換成同源的圖片,同樣也是裂開(kāi)的,解決方法很簡(jiǎn)單,遍歷svg節(jié)點(diǎn)樹(shù),將圖片都轉(zhuǎn)換成data:URL的形式即可:
// 操作svg使用了@svgdotjs/svg.js庫(kù)
const transfromImg = (svgNode) => {
let imageList = svgNode.find('image')
let task = imageList.map(async item => {
// 獲取圖片url
let imgUlr = item.attr('href') || item.attr('xlink:href')
// 已經(jīng)是data:URL形式不用轉(zhuǎn)換
if (/^data:/.test(imgUlr)) {
return
}
// 轉(zhuǎn)換并替換圖片url
let imgData = await drawToCanvas(imgUlr)
item.attr('href', imgData)
})
await Promise.all(task)
return svgNode.svg()// 返回svg html字符串
}
這里使用了前面的drawToCanvas方法來(lái)將圖片轉(zhuǎn)換成data:URL,這樣導(dǎo)出就正常了:
到這里,將純 svg 轉(zhuǎn)換為圖片就基本沒(méi)啥問(wèn)題了。
svg提供了一個(gè)foreignObject標(biāo)簽,可以插入html節(jié)點(diǎn),實(shí)際上,筆者就是使用它來(lái)實(shí)現(xiàn)節(jié)點(diǎn)的富文本編輯效果的:
接下來(lái)使用前面的方式來(lái)導(dǎo)出,結(jié)果如下:
明明顯示沒(méi)有問(wèn)題,導(dǎo)出時(shí)foreignObject內(nèi)容卻發(fā)生了偏移,這是為啥呢,其實(shí)是因?yàn)槟J(rèn)樣式的問(wèn)題,頁(yè)面全局清除了margin和padding,以及將box-sizing設(shè)置成了border-box:
那么當(dāng)svg存在于文檔樹(shù)中時(shí)是沒(méi)有問(wèn)題的,但是導(dǎo)出時(shí)使用的是svg字符串,是脫離于文檔的,所以沒(méi)有這個(gè)樣式覆蓋,那么顯示自然會(huì)出現(xiàn)問(wèn)題,知道了原因,解決方法有兩種,一是遍歷所有嵌入的html節(jié)點(diǎn),手動(dòng)添加內(nèi)聯(lián)樣式,注意一定要給所有的html節(jié)點(diǎn)都添加,只給svg、foreignObject或最外層的html節(jié)點(diǎn)添加都是不行的;第二種是直接在foreignObject標(biāo)簽里添加一個(gè)style標(biāo)簽,通過(guò)style標(biāo)簽來(lái)加上樣式,并且只要給其中一個(gè)foreignObject標(biāo)簽添加就可以了,兩種方式看你喜歡哪種,筆者使用的是第二種:
const transformForeignObject = (svgNode) => {
let foreignObjectList = svgNode.find('foreignObject')
if (foreignObjectList.length > 0) {
foreignObjectList[0].add(SVG(`<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>`))
}
return svgNode.svg()
}
導(dǎo)出結(jié)果如下:
可以看到,一切正常。
關(guān)于兼容性的問(wèn)題,筆者測(cè)試了最新的chrome、firefox、opera、safari、360急速瀏覽器,運(yùn)行都是正常的。
前面介紹的是筆者目前采用的方案,看著實(shí)現(xiàn)其實(shí)非常簡(jiǎn)單,但是過(guò)程漫長(zhǎng)且坎坷,接下來(lái),開(kāi)始我的表演。
對(duì)于svg的操作筆者使用的是svg.js庫(kù),創(chuàng)建富文本節(jié)點(diǎn)的核心代碼大致如下:
import { SVG, ForeignObject } from '@svgdotjs/svg.js'
let html = `<div>節(jié)點(diǎn)文本</div>`
let foreignObject = new ForeignObject()
foreignObject.add(SVG(html))
g.add(foreignObject)
SVG方法是用來(lái)將一段html字符串轉(zhuǎn)換為dom節(jié)點(diǎn)的。
在chrome瀏覽器和opera瀏覽器上渲染非常正常,但是在firefox瀏覽器上foreignObject標(biāo)簽的內(nèi)容完全渲染不出來(lái):
檢查元素也看不出有任何問(wèn)題,并且神奇的是只要在控制臺(tái)元素里編輯一下嵌入的html內(nèi)容,它就可以顯示了,百度搜索了一圈,也沒(méi)找到解決方法,然后因?yàn)閒irefox瀏覽器占有率并不高,于是這個(gè)問(wèn)題就擱淺了。
chrome瀏覽器雖然渲染是正常的:
但是使用前面的方式導(dǎo)出時(shí)foreignObject標(biāo)簽內(nèi)容卻是跟在firefox瀏覽器里顯示一樣是空的:
firefox能忍這個(gè)不能忍,于是嘗試使用一些將html轉(zhuǎn)換為圖片的庫(kù)。
使用html2canvas:
import html2canvas from 'html2canvas'
const useHtml2canvas = async (svgNode) => {
let el = document.createElement('div')
el.style.position = 'absolute'
el.style.left = '-9999999px'
el.appendChild(svgNode)
document.body.appendChild(el)// html2canvas轉(zhuǎn)換需要被轉(zhuǎn)換的節(jié)點(diǎn)在文檔中
let canvas = await html2canvas(el, {
backgroundColor: null
})
mdocument.body.removeChild(el)
return canvas.toDataURL()
}
html2canvas可以成功導(dǎo)出,但是存在一個(gè)問(wèn)題,就是foreignObject標(biāo)簽里的文本樣式會(huì)丟失:
這應(yīng)該是html2canvas的一個(gè)bug,不過(guò)看它這issues數(shù)量和提交記錄:
指望html2canvas改是不現(xiàn)實(shí)的,于是又嘗試使用dom-to-image:
import domtoimage from 'dom-to-image'
const dataUrl = domtoimage.toPng(el)
發(fā)現(xiàn)dom-to-image更不行,導(dǎo)出完全是空白的:
并且它上一次更新時(shí)間已經(jīng)是五六年前,所以沒(méi)辦法,只能回頭使用html2canvas。
后來(lái)有人建議使用dom-to-image-more,粗略看了一下,它是在dom-to-image庫(kù)的基礎(chǔ)上修改的,嘗試了一下,發(fā)現(xiàn)確實(shí)可以,于是就改為使用這個(gè)庫(kù),然后又有人反饋在一些瀏覽器上導(dǎo)出節(jié)點(diǎn)內(nèi)容是空的,包括firefox、360,甚至chrome之前的版本都不行,筆者只能感嘆,太難了,然后又有人建議使用上一個(gè)大版本,可以解決在firefox上的導(dǎo)出問(wèn)題,但是筆者試了一下,在其他一些瀏覽器上依舊存在問(wèn)題,于是又在考慮要不要換回html2canvas,雖然它存在一定問(wèn)題,但至少不是完全空的。
用的人多了,這個(gè)問(wèn)題又有人提了出來(lái),于是筆者又嘗試看看能不能解決,之前一直認(rèn)為是firefox瀏覽器的問(wèn)題,畢竟在chrome和opera上都是正常的,這一次就想會(huì)不會(huì)是svgjs庫(kù)的問(wèn)題,于是就去搜它的issue,沒(méi)想到,還真的搜出來(lái)了issue,大意就是因?yàn)橥ㄟ^(guò)SVG方法轉(zhuǎn)換的dom節(jié)點(diǎn)是在svg的命名空間下,也就是使用document.createElementNS方法創(chuàng)建的,導(dǎo)致部分瀏覽器渲染不出來(lái),歸根結(jié)底,這還是不同瀏覽器對(duì)于規(guī)范的不同實(shí)現(xiàn)導(dǎo)致的:
你說(shuō)chrome很強(qiáng)吧,確實(shí),但是無(wú)形中它阻止了問(wèn)題的暴露。
知道了原因,那么修改也很簡(jiǎn)單了,只要將SVG方法第二個(gè)參數(shù)設(shè)為true即可,或者自己來(lái)創(chuàng)建節(jié)點(diǎn)也可以:
foreignObject.add(document.createElemnt('div'))
果然,在firefox瀏覽器上正常渲染了。
解決了在firefox瀏覽器上foreignObject標(biāo)簽為空的問(wèn)題后,自然會(huì)懷疑之前使用img結(jié)合canvas導(dǎo)出圖片時(shí)foreignObject標(biāo)簽為空會(huì)不會(huì)也是因?yàn)檫@個(gè)問(wèn)題,同時(shí)了解了一下dom-to-image庫(kù)的實(shí)現(xiàn)原理,發(fā)現(xiàn)它也是通過(guò)將dom節(jié)點(diǎn)添加到svg的foreignObject標(biāo)簽中實(shí)現(xiàn)將html轉(zhuǎn)換成圖片的,那么就很搞笑了,我本身要轉(zhuǎn)換的內(nèi)容就是一個(gè)嵌入了foreignObject標(biāo)簽的svg,使用dom-to-image轉(zhuǎn)換,它會(huì)再次把傳給它的svg添加到一個(gè)foreignObject標(biāo)簽中,這不是套娃嗎,既然dom-to-image-more能通過(guò)foreignObject標(biāo)簽成功導(dǎo)出,那么不用它必然也可以,到這里基本確信之前不行就是因?yàn)槊臻g的問(wèn)題。
果然,在去掉了dom-to-image-more庫(kù)后,重新使用之前的方式成功導(dǎo)出了,并且在firefox、chrome、opera、360等瀏覽器中都不存在問(wèn)題,兼容性反而比dom-to-image-more庫(kù)好。
雖然筆者的實(shí)現(xiàn)很簡(jiǎn)單,但是dom-to-image-more這個(gè)庫(kù)實(shí)際上有一千多行代碼,那么它到底多做了些什么呢,點(diǎn)個(gè)關(guān)注,我們下一篇文章再見(jiàn)。
angEditor 實(shí)現(xiàn)ctrl+v粘貼圖片并上傳、word粘貼帶圖片,wangEditor實(shí)現(xiàn)粘貼word圖片,wangEditor實(shí)現(xiàn)粘貼word圖文,wangEditor實(shí)現(xiàn)粘貼word,wangEditor實(shí)現(xiàn)粘貼word文檔,wangEditor實(shí)現(xiàn)粘貼word內(nèi)容,wangEditor實(shí)現(xiàn)一鍵粘貼word圖片,wangEditor實(shí)現(xiàn)一鍵粘貼word圖文,wangEditor實(shí)現(xiàn)一鍵粘貼word,wangEditor實(shí)現(xiàn)一鍵粘貼word文檔,
我們公司有一個(gè)新聞發(fā)布系統(tǒng),每天都會(huì)安排同事在后臺(tái)發(fā)布新聞,使用頻率比較高,使用的是富文本編輯器wangEditor,
wangEditor4粘貼word圖片和文字,word圖片自動(dòng)上傳到服務(wù)器,圖片和文字HTML自動(dòng)添加到編輯器中,
現(xiàn)在有個(gè)需求,就是需要為wangEditor編輯器增加粘貼Word圖片的功能,粘貼后圖片能夠自動(dòng)上傳到服務(wù)器中,服務(wù)器地址能夠自定義
因?yàn)槁?tīng)同事反應(yīng),發(fā)布新聞的步驟有點(diǎn)繁瑣了,需要從word中復(fù)制圖文內(nèi)容,粘貼到編輯器中,然后再手動(dòng)上傳圖片,圖片數(shù)量多時(shí),這個(gè)操作太累了
現(xiàn)在就想用戶(hù)使用操作起來(lái)更方便一些,一般發(fā)新聞,或者發(fā)文章用的比較多,可以說(shuō)是高頻使用,
如果能不裝控件實(shí)現(xiàn)就最好了,不過(guò)為了減輕工作量,裝個(gè)控件也沒(méi)關(guān)系,總比一張張上傳圖片簡(jiǎn)單。
前端用了VUE2,VUE3,后端用了PHP,JSP,ASP,ASP.NET,SpringBoot
下載示例:
http://www.ncmem.com/webapp/wordpaster/versions.aspx
選擇對(duì)應(yīng)示例進(jìn)行下載:
在線代碼:https://gitee.com/xproer/wordpaster-asp.net-wangeditor
1.從示例中復(fù)制WordPaster目錄
2.復(fù)制文件上傳頁(yè)面
upload.jsp
/src/Xproer/Uploader.java
編輯器頁(yè)面:
1.引入頭和樣式
注意:不要重復(fù)引入jquery,如果您的頁(yè)面(項(xiàng)目)已經(jīng)引入了jquery,則不要重復(fù)引入jquery
2.初始化控件和插件
注意:
1.如果接口字段名稱(chēng)不是file,請(qǐng)配置FileFieldName。ueditor接口中使用的upfile字段
參考:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45
2.如果接口返回JSON,請(qǐng)配置ImageMatch
參考:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1
3.如果接口返回的圖片地址沒(méi)有域名,請(qǐng)配置ImageUrl
參考:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936
4.如果接口有權(quán)限驗(yàn)證(登陸驗(yàn)證,SESSION驗(yàn)證),請(qǐng)配置COOKIE。或取消權(quán)限驗(yàn)證。
參考:http://www.ncmem.com/doc/view.aspx?id=8602DDBF62374D189725BF17367125F3
整合后的效果:
訂閱版報(bào)價(jià)單:https://drive.weixin.qq.com/s?k=ACoAYgezAAw70JsA8m
政企版報(bào)價(jià)單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwuqJtN30#/
年費(fèi)版報(bào)價(jià)單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwFouDIB4#/
OEM版報(bào)價(jià)單:https://drive.weixin.qq.com/s?k=ACoAYgezAAwV00mQSY
產(chǎn)品源代碼:https://drive.weixin.qq.com/s?k=ACoAYgezAAwz13B5Tr
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。