fabric -- 画布实现一简单图片上传背景,拖拽点位demo

  •  点击添加一背景图
  • 添加带选择的地点,在左侧下方列表框可见
  • 点击拖动,将鼠标放入画布即可拖动地点定位
  • 左侧上方列表点击移除,即可移除点位

以下是完整代码,其中 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>
    

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值