- 点击添加一背景图
- 添加带选择的地点,在左侧下方列表框可见
- 点击拖动,将鼠标放入画布即可拖动地点定位
- 左侧上方列表点击移除,即可移除点位
以下是完整代码,其中 icon 是阿里图标库切下来的图标。
vue2的项目结构,直接将 App.vue 文件替换即可运行
需要:
npm i fabric --save
npm i nanoid --save
<template>
<div id="app">
<div
style="
padding: 10px 0;
margin: 0 auto;
display: flex;
justify-content: center;
"
>
<span>添加地点:</span>
<input type="text" v-model="pointName" />
<button @click="addPoint" style="margin-right: 20px">添加</button>
<span>添加背景图:</span>
<input type="file" id="fileDome" @change="chooseImg" />
<img
src="./assets/icon.png"
alt=""
id="img"
style="width: 20px; height: 20px"
/>
</div>
<div class="out-out">
<div class="list">
<div class="no-list">
<div
class="item"
v-for="item in pointList"
:key="item.id"
v-show="item.inMap"
>
<span>{{ item.name }}</span>
<span style="cursor: pointer; color: red" @click="removeItem(item)"
>移除</span
>
</div>
</div>
<div class="in-list">
<div
class="item"
v-for="item in pointList"
:key="item.id"
v-show="!item.inMap"
>
<span>{{ item.name }}</span>
<span style="cursor: pointer; color: blue" @click="addItem(item)"
>拖动</span
>
</div>
</div>
</div>
<div class="box">
<canvas id="c" style="border: 1px solid pink"></canvas>
</div>
</div>
</div>
</template>
<script>
import { fabric } from "fabric";
import icon from "./assets/icon.png";
import { nanoid } from "nanoid";
export default {
name: "App",
data() {
return {
canvas: null,
pointList: [],
pointName: "",
};
},
mounted() {
this.init();
fabric.Object.prototype.objectCaching = false;
},
methods: {
// 初始化 容器
init() {
const canvas = (this.canvas = new fabric.Canvas("c", {
width: document.getElementsByClassName("box")[0].getBoundingClientRect()
.width, // 画布宽度
height: document
.getElementsByClassName("box")[0]
.getBoundingClientRect().height, // 画布高度
// backgroundColor: "#eee", // 画布背景色
}));
canvas.on({
"mouse:down": (e) => {
let active = this.getObj("Active");
if (active) {
this.addActive(active);
} else {
canvas.isDragging = true;
}
},
"mouse:up": (e) => {
canvas.isDragging = false;
},
"mouse:move": (e) => {
let active = this.getObj("Active");
if (active) {
active
.set({
// left: e.absolutePointer.x,
// top: e.absolutePointer.y,
left: e.absolutePointer.x - active.width / 2,
top: e.absolutePointer.y - active.height / 2,
visible: true,
})
.setCoords();
canvas.renderAll();
} else {
if (canvas.isDragging && e && e.e && !canvas.getActiveObject()) {
let delta = new fabric.Point(e.e.movementX, e.e.movementY); // e.e.movementX 移动时画布的横向相对偏移量
canvas.relativePan(delta);
}
}
},
"mouse:wheel": (opt) => {
let delta = opt.e.deltaY; // 滚轮向上滚一下是 -100,向下滚一下是 100
let zoom = canvas.getZoom(); // 获取画布当前缩放值
// 控制缩放范围在 0.01~20 的区间内
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint(
{
x: opt.e.offsetX, // 鼠标x轴坐标
y: opt.e.offsetY, // 鼠标y轴坐标
},
zoom // 最后要缩放的值
);
},
});
},
// 选择图片
chooseImg(e) {
let _this = this;
var img = document.getElementById("img"); //获取显示图片的div元素
//这边是判断本浏览器是否支持这个API。
if (typeof FileReader === "undefined") {
console.log("抱歉,你的浏览器不支持 FileReader");
}
var file = e.target.files[0]; //获取file对象
//判断file的类型是不是图片类型。
if (!/image\/\w+/.test(file.type)) {
alert("文件必须为图片!");
return false;
}
var reader = new FileReader(); //声明一个FileReader实例
reader.readAsDataURL(file);
reader.onload = function (e) {
_this.reset();
_this.initBack(this.result);
};
},
// 初始化图标
async initBack(url) {
let canvasW = this.canvas.width;
let canvasH = this.canvas.height;
let { img, fImg } = await this.loadImg(url);
let width = img.width;
let height = img.height;
fImg.set({
scaleX: canvasW / width,
scaleY: canvasH / height,
width: width,
height: height,
// left: (canvasW - width) / 2 + 1,
// top: (canvasH - height) / 2 + 1,
left: 1,
top: 1,
});
var stroke = new fabric.Rect({
scaleX: canvasW / width,
scaleY: canvasH / height,
width: width + 1,
height: height + 1,
left: 0,
top: 0,
fill: "#ffffff",
stroke: "#fc9d9a",
strokeWidth: 1,
strokeDashArray: [10, 3],
visible: false,
type: "Stroke",
});
let group = new fabric.Group([stroke, fImg], {
selectable: false,
hoverCursor: "default",
type: "Back",
transparentCorners: true,
});
stroke.moveTo(1);
fImg.moveTo(2);
this.addBackHandle(group);
this.canvas.add(group);
},
addBackHandle(group) {
group.on("mouseover", (e) => {
this.setBackStroke(true);
});
group.on("mouseout", (e) => {
this.setBackStroke(false);
});
},
setBackStroke(val) {
this.getObj("Back")
.getObjects()
.find((item) => item.type == "Stroke")
.set({
visible: val,
});
this.canvas.renderAll();
},
getObj(type) {
return this.canvas.getObjects().find((item) => item.type == type);
},
addPoint() {
if (
this.pointName == "" ||
this.pointList.find((it) => it.name == this.pointName)
)
return;
if (!this.getObj("Back")) {
alert("请添加背景图");
return;
}
let obj = {
name: this.pointName,
id: nanoid(),
inMap: false,
};
this.pointList.unshift(obj);
this.pointName = "";
},
// 添加图标
async addItem(info) {
let textLength = String(info.name).length;
let text = new fabric.Text(info.name, {
fill: "rgb(255, 0, 0, 1)",
fontSize: 16,
isText: true,
});
text.set({
isText: true,
});
let { img, fImg } = await this.loadImg(icon);
fImg.set({
scaleX: 30 / img.width,
scaleY: 30 / img.height,
top: 20,
left: (textLength * 16 - 30) / 2,
});
let group = new fabric.Group([text, fImg], {
left: 300,
top: 300,
transparentCorners: false,
cornerSize: 8,
type: "Active",
id: info.id,
lockScalingFlip: true,
lockRotation: true, //禁止旋转
lockScalingX: true,
lockScalingY: true,
visible: false,
});
group.hasControls = false;
group.hasBorders = false;
group.moveTo(999);
this.canvas.add(group);
},
addActive(active) {
active = active ? active : this.getObj("Active");
if (!active) {
return false;
}
active.set({
type: "1",
inMap: true,
});
// 将未添加位置的监区移到已添加的
this.pointList.forEach((item) => {
if (item.id == active.id) {
this.$set(item, "inMap", true);
}
});
this.canvas.setActiveObject(active);
this.canvas.renderAll();
},
removeItem(item) {
let it = this.pointList.find(el => el.id == item.id)
it && ( this.$set(it,'inMap',false))
let active = this.canvas.getObjects().find(el => el.id == item.id)
active && this.canvas.remove(active)
this.canvas.renderAll()
},
loadImg(imgSrc) {
return new Promise((resolve, reject) => {
let iconImg = new Image();
iconImg.src = imgSrc;
iconImg.onload = () => {
fabric.Image.fromURL(imgSrc, (oImg) => {
resolve({
img: iconImg,
fImg: oImg,
});
});
};
});
},
reset() {
this.canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
this.canvas.clear();
},
},
};
</script>
<style lang="less">
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
* {
box-sizing: border-box;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* text-align: center; */
color: #2c3e50;
/* margin-top: 60px; */
padding: 0;
margin: 0;
width: 100%;
height: 100%;
position: relative;
box-sizing: border-box;
}
.list {
float: left;
margin: 0 20px;
}
.no-list {
margin-bottom: 20px;
}
.no-list,
.in-list {
width: 200px;
height: 280px;
border: 1px solid black;
}
.item {
width: 100%;
height: 20px;
padding:2px 10px;
display: flex;
justify-content: space-between;
}
.box {
float: left;
width: 800px;
height: 600px;
overflow: hidden; // 使用 html 方式插入图片
/* background-color: pink; */
#c {
width: 100%;
height: 100%;
}
}
</style>