vue下如何利用canvas實現(xiàn)在線圖片標(biāo)注
web端實現(xiàn)在線圖片標(biāo)注在此做下記錄,功能類似微信截圖時的標(biāo)注,包含畫線、框、箭頭和文字輸入,思路是利用canvas畫布,先把要標(biāo)注的圖片使用drawImage方法畫在畫布上,然后定義畫線、框、箭頭和文字輸入的方法調(diào)用
組件代碼如下
<template> ? <div class="draw"> ? ? <div class="drawTop" ref="drawTop" v-if="lineStep == lineNum"> ? ? ? <div> ? ? ? ? <el-button type @click="resetAll">清空</el-button> ? ? ? ? <el-button type @click="repeal">撤銷</el-button> ? ? ? ? <el-button type @click="canvasRedo">恢復(fù)</el-button> ? ? ? ? <el-button type @click="downLoad">下載</el-button> ? ? ? </div> ? ? ? <div style="width:22%"> ? ? ? ? 選擇繪制類型: ? ? ? ? <el-radio-group v-model="type" size="medium"> ? ? ? ? ? <el-radio-button ? ? ? ? ? ? v-for="(item,index) in typeOption" ? ? ? ? ? ? :key="index" ? ? ? ? ? ? :label="item.value" ? ? ? ? ? ? @click.native="radioClick(item.value)" ? ? ? ? ? >{{item.label}} ? ? ? ? ? </el-radio-button> ? ? ? ? </el-radio-group> ? ? ? </div> ? ? ? <div style="width:15%"> ? ? ? ? 邊框粗細(xì): ? ? ? ? <el-slider v-model="lineWidth" :min="0" :max="10" :step="1" style="width:70%"></el-slider> ? ? ? </div> ? ? ? <div> ? ? ? ? 線條顏色: ? ? ? ? <el-color-picker v-model="strokeStyle"></el-color-picker> ? ? ? </div> ? ? ? <div> ? ? ? ? 文字顏色: ? ? ? ? <el-color-picker v-model="fontColor"></el-color-picker> ? ? ? </div> ? ? ? <div style="width:15%"> ? ? ? ? 文字大小: ? ? ? ? <el-slider v-model="fontSize" :min="14" :max="36" :step="2" style="width:70%"></el-slider> ? ? ? </div> ? ? </div> ? ? <div style="height: 100%;width: 100%;position:relative;"> ? ? ? <div class="content"></div> ? ? ? <input v-show="isShow" type="text" @blur="txtBlue" ref="txt" id="txt" ? ? ? ? ? ? ?style="z-index: 9999;position: absolute;border: 0;background:none;outline: none;"/> ? ? </div> ? </div> </template>
<script> ? export default { ? ? name: "callout", ? ? props: { ? ? ? imgPath: undefined, ? ? ? }, ? ? data() { ? ? ? return { ? ? ? ? isShow: false, ? ? ? ? canvas: "", ? ? ? ? ctx: "", ? ? ? ? ctxX: 0, ? ? ? ? ctxY: 0, ? ? ? ? lineWidth: 1, ? ? ? ? type: "L", ? ? ? ? typeOption: [ ? ? ? ? ? {label: "線", value: "L"}, ? ? ? ? ? {label: "矩形", value: "R"}, ? ? ? ? ? {label: "箭頭", value: "A"}, ? ? ? ? ? {label: "文字", value: "T"}, ? ? ? ? ], ? ? ? ? canvasHistory: [], ? ? ? ? step: 0, ? ? ? ? loading: false, ? ? ? ? fillStyle: "#CB0707", ? ? ? ? strokeStyle: "#CB0707", ? ? ? ? lineNum: 2, ? ? ? ? linePeak: [], ? ? ? ? lineStep: 2, ? ? ? ? ellipseR: 0.5, ? ? ? ? dialogVisible: false, ? ? ? ? isUnfold: true, ? ? ? ? fontSize: 24, ? ? ? ? fontColor: "#CB0707", ? ? ? ? fontFamily: '微軟雅黑', ? ? ? ? img: new Image(), ? ? ? }; ? ? }, ? ? mounted() { ? ? ? let _this = this; ? ? ? let image = new Image(); ? ? ? image.setAttribute('crossOrigin', 'anonymous'); ? ? ? image.src = this.imgPath; ? ? ? image.onload = function () {//圖片加載完,再draw 和 toDataURL ? ? ? ? if (image.complete) { ? ? ? ? ? _this.img = image ? ? ? ? ? let content = document.getElementsByClassName("content")[0]; ? ? ? ? ? _this.canvas = document.createElement("canvas"); ? ? ? ? ? _this.canvas.height = _this.img.height ? ? ? ? ? _this.canvas.width = _this.img.width ? ? ? ? ? _this.ctx = _this.canvas.getContext("2d"); ? ? ? ? ? _this.ctx.globalAlpha = 1; ? ? ? ? ? _this.ctx.drawImage(_this.img, 0, 0) ? ? ? ? ? _this.canvasHistory.push(_this.canvas.toDataURL()); ? ? ? ? ? _this.ctx.globalCompositeOperation = _this.type; ? ? ? ? ? content.appendChild(_this.canvas); ? ? ? ? ? _this.bindEventLisner(); ? ? ? ? } ? ? ? } ? ? }, ? ? methods: { ? ? ? radioClick(item) { ? ? ? ? if (item != "T") { ? ? ? ? ? this.txtBlue() ? ? ? ? ? this.resetTxt() ? ? ? ? } ? ? ? }, ? ? ? // 下載畫布 ? ? ? downLoad() { ? ? ? ? let _this = this; ? ? ? ? let url = _this.canvas.toDataURL("image/png"); ? ? ? ? let fileName = "canvas.png"; ? ? ? ? if ("download" in document.createElement("a")) { ? ? ? ? ? // 非IE下載 ? ? ? ? ? const elink = document.createElement("a"); ? ? ? ? ? elink.download = fileName; ? ? ? ? ? elink.style.display = "none"; ? ? ? ? ? elink.href = url; ? ? ? ? ? document.body.appendChild(elink); ? ? ? ? ? elink.click(); ? ? ? ? ? document.body.removeChild(elink); ? ? ? ? } else { ? ? ? ? ? // IE10+下載 ? ? ? ? ? navigator.msSaveBlob(url, fileName); ? ? ? ? } ? ? ? }, ? ? ? // 清空畫布及歷史記錄 ? ? ? resetAll() { ? ? ? ? this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ? ? ? ? this.canvasHistory = []; ? ? ? ? this.ctx.drawImage(this.img, 0, 0); ? ? ? ? this.canvasHistory.push(this.canvas.toDataURL()); ? ? ? ? this.step = 0; ? ? ? ? this.resetTxt(); ? ? ? }, ? ? ? // 清空當(dāng)前畫布 ? ? ? reset() { ? ? ? ? this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ? ? ? ? this.ctx.drawImage(this.img, 0, 0); ? ? ? ? this.resetTxt(); ? ? ? }, ? ? ? // 撤銷方法 ? ? ? repeal() { ? ? ? ? let _this = this; ? ? ? ? if (this.isShow) { ? ? ? ? ? _this.resetTxt(); ? ? ? ? ? _this._repeal(); ? ? ? ? } else { ? ? ? ? ? _this._repeal(); ? ? ? ? } ? ? ? ? }, ? ? ? _repeal() { ? ? ? ? if (this.step >= 1) { ? ? ? ? ? this.step = this.step - 1; ? ? ? ? ? let canvasPic = new Image(); ? ? ? ? ? canvasPic.src = this.canvasHistory[this.step]; ? ? ? ? ? canvasPic.addEventListener("load", () => { ? ? ? ? ? ? this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ? ? ? ? ? ? this.ctx.drawImage(canvasPic, 0, 0); ? ? ? ? ? ? this.loading = true; ? ? ? ? ? }); ? ? ? ? } else { ? ? ? ? ? this.$message.warning("不能再繼續(xù)撤銷了"); ? ? ? ? } ? ? ? }, ? ? ? // 恢復(fù)方法 ? ? ? canvasRedo() { ? ? ? ? if (this.step < this.canvasHistory.length - 1) { ? ? ? ? ? if (this.step == 0) { ? ? ? ? ? ? this.step = 1; ? ? ? ? ? } else { ? ? ? ? ? ? this.step++; ? ? ? ? ? } ? ? ? ? ? let canvasPic = new Image(); ? ? ? ? ? canvasPic.src = this.canvasHistory[this.step]; ? ? ? ? ? canvasPic.addEventListener("load", () => { ? ? ? ? ? ? this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ? ? ? ? ? ? this.ctx.drawImage(canvasPic, 0, 0); ? ? ? ? ? }); ? ? ? ? } else { ? ? ? ? ? this.$message.warning("已經(jīng)是最新的記錄了"); ? ? ? ? } ? ? ? }, ? ? ? // 繪制歷史數(shù)組中的最后一個 ? ? ? rebroadcast() { ? ? ? ? let canvasPic = new Image(); ? ? ? ? canvasPic.src = this.canvasHistory[this.step]; ? ? ? ? canvasPic.addEventListener("load", () => { ? ? ? ? ? this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ? ? ? ? ? this.ctx.drawImage(canvasPic, 0, 0); ? ? ? ? ? this.loading = true; ? ? ? ? }); ? ? ? }, ? ? ? // 綁定事件,判斷分支 ? ? ? bindEventLisner() { ? ? ? ? let _this = this; ? ? ? ? let r1, r2; // 繪制圓形,矩形需要 ? ? ? ? this.canvas.onmousedown = function (e) { ? ? ? ? ? console.log("onmousedown"); ? ? ? ? ? if (_this.type == "L") { ? ? ? ? ? ? _this.createL(e, "begin"); ? ? ? ? ? } else if (_this.type == "R") { ? ? ? ? ? ? r1 = e.layerX; ? ? ? ? ? ? r2 = e.layerY; ? ? ? ? ? ? _this.createR(e, "begin", r1, r2); ? ? ? ? ? } else if (_this.type == "A") { ? ? ? ? ? ? _this.drawArrow(e, "begin") ? ? ? ? ? } else if (_this.type == "T") { ? ? ? ? ? ? _this.createT(e, "begin") ? ? ? ? ? } ? ? ? ? }; ? ? ? ? this.canvas.onmouseup = function (e) { ? ? ? ? ? console.log("onmouseup"); ? ? ? ? ? if (_this.type == "L") { ? ? ? ? ? ? _this.createL(e, "end"); ? ? ? ? ? } else if (_this.type == "R") { ? ? ? ? ? ? _this.createR(e, "end", r1, r2); ? ? ? ? ? ? r1 = null; ? ? ? ? ? ? r2 = null; ? ? ? ? ? } else if (_this.type == "A") { ? ? ? ? ? ? _this.drawArrow(e, "end") ? ? ? ? ? } else if (_this.type == "T") { ? ? ? ? ? ? _this.createT(e, "end") ? ? ? ? ? } ? ? ? ? }; ? ? ? ? }, ? ? ? // 繪制線條 ? ? ? createL(e, status) { ? ? ? ? let _this = this; ? ? ? ? if (status == "begin") { ? ? ? ? ? _this.ctx.beginPath(); ? ? ? ? ? _this.ctx.moveTo(e.layerX, e.layerY); ? ? ? ? ? _this.canvas.onmousemove = function (e) { ? ? ? ? ? ? console.log("onmousemove"); ? ? ? ? ? ? _this.ctx.lineTo(e.layerX, e.layerY); ? ? ? ? ? ? _this.ctx.strokeStyle = _this.strokeStyle; ? ? ? ? ? ? _this.ctx.lineWidth = _this.lineWidth; ? ? ? ? ? ? _this.ctx.stroke(); ? ? ? ? ? }; ? ? ? ? } else if (status == "end") { ? ? ? ? ? _this.ctx.closePath(); ? ? ? ? ? _this.step = _this.step + 1; ? ? ? ? ? if (_this.step < _this.canvasHistory.length - 1) { ? ? ? ? ? ? _this.canvasHistory.length = _this.step; // 截斷數(shù)組 ? ? ? ? ? } ? ? ? ? ? _this.canvasHistory.push(_this.canvas.toDataURL()); ? ? ? ? ? _this.canvas.onmousemove = null; ? ? ? ? } ? ? ? }, ? ? ? // 繪制矩形 ? ? ? createR(e, status, r1, r2) { ? ? ? ? let _this = this; ? ? ? ? let r; ? ? ? ? if (status == "begin") { ? ? ? ? ? console.log("onmousemove"); ? ? ? ? ? _this.canvas.onmousemove = function (e) { ? ? ? ? ? ? _this.reset(); ? ? ? ? ? ? let rx = e.layerX - r1; ? ? ? ? ? ? let ry = e.layerY - r2; ? ? ? ? ? ? ? //保留之前繪畫的圖形 ? ? ? ? ? ? if (_this.step !== 0) { ? ? ? ? ? ? ? let canvasPic = new Image(); ? ? ? ? ? ? ? canvasPic.src = _this.canvasHistory[_this.step]; ? ? ? ? ? ? ? _this.ctx.drawImage(canvasPic, 0, 0); ? ? ? ? ? ? } ? ? ? ? ? ? ? _this.ctx.beginPath(); ? ? ? ? ? ? _this.ctx.strokeRect(r1, r2, rx, ry); ? ? ? ? ? ? _this.ctx.strokeStyle = _this.strokeStyle; ? ? ? ? ? ? _this.ctx.lineWidth = _this.lineWidth; ? ? ? ? ? ? _this.ctx.closePath(); ? ? ? ? ? ? _this.ctx.stroke(); ? ? ? ? ? }; ? ? ? ? } else if (status == "end") { ? ? ? ? ? _this.rebroadcast(); ? ? ? ? ? let interval = setInterval(() => { ? ? ? ? ? ? if (_this.loading) { ? ? ? ? ? ? ? clearInterval(interval); ? ? ? ? ? ? ? _this.loading = false; ? ? ? ? ? ? } else { ? ? ? ? ? ? ? return; ? ? ? ? ? ? } ? ? ? ? ? ? let rx = e.layerX - r1; ? ? ? ? ? ? let ry = e.layerY - r2; ? ? ? ? ? ? _this.ctx.beginPath(); ? ? ? ? ? ? _this.ctx.rect(r1, r2, rx, ry); ? ? ? ? ? ? _this.ctx.strokeStyle = _this.strokeStyle; ? ? ? ? ? ? _this.ctx.lineWidth = _this.lineWidth; ? ? ? ? ? ? _this.ctx.closePath(); ? ? ? ? ? ? _this.ctx.stroke(); ? ? ? ? ? ? _this.step = _this.step + 1; ? ? ? ? ? ? if (_this.step < _this.canvasHistory.length - 1) { ? ? ? ? ? ? ? _this.canvasHistory.length = _this.step; // 截斷數(shù)組 ? ? ? ? ? ? } ? ? ? ? ? ? _this.canvasHistory.push(_this.canvas.toDataURL()); ? ? ? ? ? ? _this.canvas.onmousemove = null; ? ? ? ? ? }, 1); ? ? ? ? } ? ? ? }, ? ? ? ? //繪制箭頭 ? ? ? drawArrow(e, status) { ? ? ? ? let _this = this; ? ? ? ? if (status == "begin") { ? ? ? ? ? //獲取起始位置 ? ? ? ? ? _this.arrowFromX = e.layerX; ? ? ? ? ? _this.arrowFromY = e.layerY; ? ? ? ? ? _this.ctx.beginPath(); ? ? ? ? ? _this.ctx.moveTo(e.layerX, e.layerY); ? ? ? ? } else if (status == "end") { ? ? ? ? ? //計算箭頭及畫線 ? ? ? ? ? let toX = e.layerX; ? ? ? ? ? let toY = e.layerY; ? ? ? ? ? let theta = 30; ? ? ? ? ? let headlen = 10; ? ? ? ? ? let _this = this; ? ? ? ? ? let fromX = this.arrowFromX; ? ? ? ? ? let fromY = this.arrowFromY; ? ? ? ? ? // 計算各角度和對應(yīng)的P2,P3坐標(biāo) ? ? ? ? ? let angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI, ? ? ? ? ? angle1 = (angle + theta) * Math.PI / 180, ? ? ? ? ? angle2 = (angle - theta) * Math.PI / 180, ? ? ? ? ? topX = headlen * Math.cos(angle1), ? ? ? ? ? topY = headlen * Math.sin(angle1), ? ? ? ? ? botX = headlen * Math.cos(angle2), ? ? ? ? ? botY = headlen * Math.sin(angle2); ? ? ? ? ? let arrowX = fromX - topX, ? ? ? ? ? arrowY = fromY - topY; ? ? ? ? ? _this.ctx.moveTo(arrowX, arrowY); ? ? ? ? ? _this.ctx.moveTo(fromX, fromY); ? ? ? ? ? _this.ctx.lineTo(toX, toY); ? ? ? ? ? arrowX = toX + topX; ? ? ? ? ? arrowY = toY + topY; ? ? ? ? ? _this.ctx.moveTo(arrowX, arrowY); ? ? ? ? ? _this.ctx.lineTo(toX, toY); ? ? ? ? ? arrowX = toX + botX; ? ? ? ? ? arrowY = toY + botY; ? ? ? ? ? _this.ctx.lineTo(arrowX, arrowY); ? ? ? ? ? _this.ctx.strokeStyle = _this.strokeStyle; ? ? ? ? ? _this.ctx.lineWidth = _this.lineWidth; ? ? ? ? ? _this.ctx.stroke(); ? ? ? ? ? ? _this.ctx.closePath(); ? ? ? ? ? _this.step = _this.step + 1; ? ? ? ? ? if (_this.step < _this.canvasHistory.length - 1) { ? ? ? ? ? ? _this.canvasHistory.length = _this.step; // 截斷數(shù)組 ? ? ? ? ? } ? ? ? ? ? _this.canvasHistory.push(_this.canvas.toDataURL()); ? ? ? ? ? _this.canvas.onmousemove = null; ? ? ? ? } ? ? ? }, ? ? ? ? //文字輸入 ? ? ? createT(e, status) { ? ? ? ? let _this = this; ? ? ? ? if (status == "begin") { ? ? ? ? ? } else if (status == "end") { ? ? ? ? ? let offset = 0; ? ? ? ? ? if (_this.fontSize >= 28) { ? ? ? ? ? ? offset = (_this.fontSize / 2) - 3 ? ? ? ? ? } else { ? ? ? ? ? ? offset = (_this.fontSize / 2) - 2 ? ? ? ? ? } ? ? ? ? ? ? _this.ctxX = e.layerX + 2; ? ? ? ? ? _this.ctxY = e.layerY + offset; ? ? ? ? ? ? let index = this.getPointOnCanvas(e); ? ? ? ? ? _this.$refs.txt.style.left = index.x + 'px'; ? ? ? ? ? _this.$refs.txt.style.top = index.y - (_this.fontSize / 2) + 'px'; ? ? ? ? ? _this.$refs.txt.value = ''; ? ? ? ? ? _this.$refs.txt.style.height = _this.fontSize + "px"; ? ? ? ? ? _this.$refs.txt.style.width = _this.canvas.width - e.layerX - 1 + "px", ? ? ? ? ? ? _this.$refs.txt.style.fontSize = _this.fontSize + "px"; ? ? ? ? ? _this.$refs.txt.style.fontFamily = _this.fontFamily; ? ? ? ? ? _this.$refs.txt.style.color = _this.fontColor; ? ? ? ? ? _this.$refs.txt.style.maxlength = Math.floor((_this.canvas.width - e.layerX) / _this.fontSize); ? ? ? ? ? _this.isShow = true; ? ? ? ? ? setTimeout(() => { ? ? ? ? ? ? _this.$refs.txt.focus(); ? ? ? ? ? }) ? ? ? ? } ? ? ? }, ? ? ? //文字輸入框失去光標(biāo)時在畫布上生成文字 ? ? ? txtBlue() { ? ? ? ? let _this = this; ? ? ? ? let txt = _this.$refs.txt.value; ? ? ? ? if (txt) { ? ? ? ? ? _this.ctx.font = _this.$refs.txt.style.fontSize + ' ' + _this.$refs.txt.style.fontFamily; ? ? ? ? ? _this.ctx.fillStyle = _this.$refs.txt.style.color; ? ? ? ? ? _this.ctx.fillText(txt, _this.ctxX, _this.ctxY); ? ? ? ? ? _this.step = _this.step + 1; ? ? ? ? ? if (_this.step < _this.canvasHistory.length - 1) { ? ? ? ? ? ? _this.canvasHistory.length = _this.step; // 截斷數(shù)組 ? ? ? ? ? } ? ? ? ? ? _this.canvasHistory.push(_this.canvas.toDataURL()); ? ? ? ? ? _this.canvas.onmousemove = null; ? ? ? ? } ? ? ? }, ? ? ? //計算文字框定位位置 ? ? ? getPointOnCanvas(e) { ? ? ? ? let cs = this.canvas; ? ? ? ? let content = document.getElementsByClassName("content")[0]; ? ? ? ? return { ? ? ? ? ? x: e.layerX + (content.clientWidth - cs.width) / 2, ? ? ? ? ? y: e.layerY ? ? ? ? }; ? ? ? }, ? ? ? //清空文字 ? ? ? resetTxt() { ? ? ? ? let _this = this; ? ? ? ? _this.$refs.txt.value = ''; ? ? ? ? _this.isShow = false; ? ? ? } ? ? } ? }; </script>
<style scope> ? * { ? ? box-sizing: border-box; ? } ? ? body, ? html, ? #app { ? ? overflow: hidden; ? } ? ? .draw { ? ? height: 100%; ? ? min-width: 420px; ? ? display: flex; ? ? flex-direction: column; ? } ? ? .content { ? ? flex-grow: 1; ? ? height: 100%; ? ? width: 100%; ? } ? ? .drawTop { ? ? display: flex; ? ? justify-content: flex-start; ? ? align-items: center; ? ? padding: 5px; ? ? height: 52px; ? } ? ? .drawTop > div { ? ? display: flex; ? ? align-items: center; ? ? padding: 5px 5px; ? } ? ? div.drawTopContrllor { ? ? display: none; ? } ? ? @media screen and (max-width: 1200px) { ? ? .drawTop { ? ? ? position: absolute; ? ? ? background-color: white; ? ? ? width: 100%; ? ? ? flex-direction: column; ? ? ? align-items: flex-start; ? ? ? height: 30px; ? ? ? overflow: hidden; ? ? } ? ? ? .drawTopContrllor { ? ? ? display: flex !important; ? ? ? height: 30px; ? ? ? width: 100%; ? ? ? justify-content: center; ? ? ? align-items: center; ? ? ? padding: 0 !important; ? ? } ? } </style>
然后在頁面中引入組件,傳入圖片鏈接。
在開發(fā)過程中遇到的問題
文字輸入功能在用戶輸入文字后,如果不再點擊別的地方直接點擊別的功能按鈕的話,最后輸入的文字將不會再畫布上生成,通過監(jiān)控輸入框的blur事件來在畫布上生成文字,避免這個問題。
文字輸入時字體的大小會影響生成文字的位置,這里發(fā)現(xiàn)文字的大小和位置有一個偏移量:
let offset = 0; if (_this.fontSize >= 28) { ? offset = (_this.fontSize / 2) - 3 } else { ? offset = (_this.fontSize / 2) - 2 }
在畫布上生成文字的時候需要加上這個偏移量,這里字體范圍是14~36,別的字體大小沒有校驗,不一定適用這個計算方式。
繪制矩形的時候需要先清空畫布,在清空之前先保存一次畫布然后再清空再重新畫一下畫布,負(fù)責(zé)矩形框會不停的出現(xiàn)軌跡,并且之前畫的元素會消失。
撤銷的時候需要考慮文字輸入,判斷input得v-show是否為true,如果是true需要先清空文字,再撤銷,否則畫布上會一直存在一個輸入框。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于vue-cli、elementUI的Vue超簡單入門小例子(推薦)
這篇文章主要介紹了基于vue-cli、elementUI的Vue超簡單入門小例子,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04vue打包的時候自動將px轉(zhuǎn)成rem的操作方法
這篇文章主要介紹了vue打包的時候自動將px轉(zhuǎn)成rem的操作方法,需要的朋友可以參考下2018-06-06vue內(nèi)置組件transition簡單原理圖文詳解(小結(jié))
這篇文章主要介紹了vue內(nèi)置組件transition簡單原理圖文詳解(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07vue-element-admin項目導(dǎo)入和導(dǎo)出的實現(xiàn)
組件是Vue.js最強(qiáng)大的功能,這篇文章主要介紹了vue-element-admin項目導(dǎo)入和導(dǎo)出的實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-05-05