自学内容网 自学内容网

使用拖拽生成活动海报(vue项目)

<template>
  <div class="poster-editor">
    <div class="toolbar" v-if="edit == '1'">
      <div style="text-align: center;font-size: 14px;font-weight: 700;margin-bottom: 10px;">工具栏</div>
      <div class="toolbar-item" v-for="(item, index) in toolbarItems" :key="index"
        @dragstart="onDragStart($event, item)" draggable="true">
        <span>{{ item.label }}</span>
        <el-popover placement="top" width="350" trigger="click">
          <div style="margin-bottom: 5px;">
            <el-input v-model="item.fontSize" placeholder="文字大小" @input="item.fontSize = integer(item.fontSize)"
              @blur="clickFontSizeColor(item)"  @change="clickFontSizeColor(item)">
              <template slot="prepend">文字大小</template>
            </el-input>
          </div>
          <div style="display: flex;">
            <el-input v-model="item.color" placeholder="颜色" @blur="clickFontSizeColor(item)" @change="clickFontSizeColor(item)">
              <template slot="prepend">文字颜色</template>
            </el-input>
            <el-color-picker v-model="item.color" @change="clickFontSizeColor(item)" style="margin-left: 10px;"></el-color-picker>
          </div>
          <el-button v-if="item.type == 'text'" slot="reference" type="text" icon="el-icon-edit"></el-button>
        </el-popover>
      </div>
    </div>

    <div class="poster-canvas" ref="posterCanvas" @drop="onDrop($event)" @dragover="onDragOver($event)">
      <div class="background" :style="{ backgroundImage: `url(${backgroundImage})` }"></div>
      <div v-for="(element, index) in elements" :key="index" class="poster-element" :style="{
      top: element.top + 'px',
      left: element.left + 'px',
      width: element.width + 'px',
      height: element.height + 'px',
      transform: `rotate(${element.rotation}deg)`
    }" @mousedown="onMouseDown($event, index)">
        <button class="delete-btn" @click.stop="onDeleteElement(index)" v-if="edit == '1'">x</button>
        <img v-if="element.type === 'image'" :src="element.content" class="draggable-image" />
        <div v-else :style="{ fontSize: element.fontSize + 'px', color: element.color }" class="draggable-text">{{
      element.content }}</div>
      </div>
    </div>
  </div>
</template>

<script>
// import html2canvas from 'html2canvas'
export default {
  props: {
    backgroundImage: {
      type: String,
      default: ''
    },
    edit: {
      type: String,
      default: ''
    }
  },
  watch: {

  },
  data() {
    return {
      toolbarItems: [
        { label: '二维码', type: 'image', content: require("../../../../assets/img/card_hm.png") },
        { label: '名称', type: 'text', content: '123', fontSize: 14, color: '#000000' },
        { label: '电话', type: 'text', content: '123', fontSize: 14, color: '#000000' },
        { label: '地址', type: 'text', content: '123', fontSize: 14, color: '#000000' }
      ],
      elements: [],
      currentElementIndex: null,
      offsetX: 0,
      offsetY: 0,
      isDragging: false,
      isResizing: false,
      isRotating: false,
      startWidth: 0,
      startHeight: 0,
      startFontSize: 16,
      startRotation: 0,
      resizeStartX: 0,
      resizeStartY: 0,
      rotateStartX: 0,
      rotateStartY: 0,
      // backgroundImage: '', // 背景图片
    };
  },
  methods: {
    onDragStart(event, item) {
      const itemJSON = JSON.stringify(item);
      event.dataTransfer.setData('element', itemJSON);
    },
    onDragOver(event) {
      event.preventDefault();
    },
    onDrop(event) {
      event.preventDefault();
      const canvasRect = this.$refs.posterCanvas.getBoundingClientRect();
      const elementDataString = event.dataTransfer.getData('element');

      if (!elementDataString) {
        console.error('No data found in drag event');
        return; // 提早返回,避免解析错误
      }

      let elementData;
      try {
        elementData = JSON.parse(elementDataString);
      } catch (error) {
        console.error('Failed to parse element data:', error);
        return; // 处理解析错误
      }

      // console.log('elementData:', elementData);

      let newElement = {
        ...elementData,
        top: event.clientY - canvasRect.top,
        left: event.clientX - canvasRect.left,
        width: elementData.type === 'image' ? 100 : 150,
        height: elementData.type === 'image' ? 100 : 40,
        fontSize: elementData.type === 'text' ? elementData.fontSize : '',
        color: elementData.type === 'text' ? elementData.color : '',
        rotation: 0, // 新增旋转属性
      };
      this.elements.push(newElement);
      this.$emit('savePoster', this.elements);
    },
    onMouseDown(event, index) {
      // console.log(event, index,'event, index')
      this.currentElementIndex = index;
      this.offsetX = event.clientX - (this.elements[index].left);
      this.offsetY = event.clientY - (this.elements[index].top);
      this.isDragging = true;
      document.addEventListener('mousemove', this.onMouseMove);
      document.addEventListener('mouseup', this.onMouseUp);
      // 停止并更新
      this.$emit('savePoster', this.elements);
    },
    onMouseMove(event) {
      if (this.isDragging && this.currentElementIndex !== null) {
        const fixedWidth = 400; // 固定宽度
        const fixedHeight = 800; // 固定高度
        const element = this.elements[this.currentElementIndex];

        // 计算新的位置
        let newTop = event.clientY - this.offsetY;
        let newLeft = event.clientX - this.offsetX;

        // 限制移动范围(上下左右)
        if (newTop < 0) newTop = 0; // 上边界
        if (newLeft < 0) newLeft = 0; // 左边界

        console.log()
        if (newTop + element.height > fixedHeight) {
          newTop = fixedHeight - element.height; // 下边界
        }
        if (newLeft + element.width > fixedWidth) {
          newLeft = fixedWidth - element.width; // 右边界
        }

        // 更新元素位置
        this.$set(this.elements, this.currentElementIndex, {
          ...element,
          top: newTop,
          left: newLeft,
        });
      }
    },
    onMouseUp() {
      this.isDragging = false;
      this.isResizing = false;
      this.isRotating = false;
      document.removeEventListener('mousemove', this.onMouseMove);
      document.removeEventListener('mouseup', this.onMouseUp);
    },
    onResizeStart(event, index) {
      this.currentElementIndex = index;
      this.isResizing = true;
      const element = this.elements[index];
      this.startWidth = element.width;
      this.startHeight = element.height;
      this.resizeStartX = event.clientX;
      this.resizeStartY = event.clientY;
      document.addEventListener('mousemove', this.onMouseMove);
      document.addEventListener('mouseup', this.onMouseUp);
    },
    onRotateStart(event, index) {
      this.currentElementIndex = index;
      this.isRotating = true;
      const element = this.elements[index];
      this.startRotation = element.rotation;
      this.rotateStartX = event.clientX;
      this.rotateStartY = event.clientY;
      document.addEventListener('mousemove', this.onMouseMove);
      document.addEventListener('mouseup', this.onMouseUp);
    },
    onDeleteElement(index) {
      this.elements.splice(index, 1);
    },
    //修改文字大小 
    clickFontSizeColor(item) {
      if (this.elements.length > 0) {
        this.elements.forEach(element => {
          if (element.label == item.label) {
            element.fontSize = item.fontSize
            element.color = item.color
          }
        });
        this.$emit('savePoster', this.elements);
      }
    },
  },
};
</script>

<style lang="less" scoped>
.poster-editor {
  display: flex;
  padding: 20px;
  margin-top: 20px;
}

.toolbar {
  width: 125px;
  background-color: #f5f5f5;
  padding: 10px;
}

.toolbar-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  text-align: left;
  margin-bottom: 10px;
  padding: 10px;
  background-color: #e1e1e1;
  border-radius: 5px;
  cursor: grab;
}

.poster-canvas {
  position: relative;
  width: 400px;
  height: 800px;
  background-color: #ffffff;
  border: 1px solid #ddd;
  border-radius: 40px;
  overflow: hidden;
  margin-left: 20px;
}


.background {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  // width: 400px;
  // height: 800px;
  background-size: 100% 100%;
  background-position: center;
}

.poster-element {
  position: absolute;
  cursor: move;
  box-sizing: border-box;
}

.poster-element:hover {
  .delete-btn {
    display: block;
  }

  .draggable-tex {
    border: 1px solid #ccc;
    background-color: rgba(240, 240, 240, 0.8);
  }
}

.draggable-image {
  width: 100%;
  height: 100%;
}

.draggable-text {
  text-align: left;
  // padding: 10px;
  // line-height: 20px;
  // 下面才是重点关注代码
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  text-overflow: ellipsis;
  white-space: normal;
  -webkit-line-clamp: 4;
  /* 设置最大显示行数 */
}

.delete-btn {
  position: absolute;
  top: -10px;
  right: -10px;
  background-color: red;
  color: white;
  border: none;
  border-radius: 50%;
  width: 20px;
  height: 20px;
  cursor: pointer;
  font-size: 12px;
  display: none;
}

.resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 10px;
  height: 10px;
  background-color: blue;
  cursor: se-resize;
}

.rotate-handle {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 10px;
  height: 10px;
  background-color: green;
  cursor: grab;
}

.controls {
  margin: 20px;
}
</style>

使用

// 拖拽画布

import Dragcanvas from "./components/dragcanvas.vue";
 <Dragcanvas @savePoster='savePoster' :backgroundImage="ruleForm.cover" :edit="$route.query.edit"></Dragcanvas>
 components: {
    Dragcanvas
  },


    // 活码拖拽
    savePoster(item) {
      let width = 400 // 固定宽
      let height = 800 // 固定高度
      let ary = []
      item.map(it => {
        ary.push({
          content: it.content,
          fontSize: it.fontSize,
          label: it.label,
          rotation: it.rotation,
          type: it.type,
          width: it.width,
          height: it.height,
          left: it.left,
          top: it.top,
          fixedwidth: width,
          fixedheight: height,
          leftpercentage: (it.left / width) * 100,
          toppercentage: (it.top / height) * 100,
          color: it.color,
        })
      })
      console.log(ary, '拖拽的数据')
    }

展示效果

在这里插入图片描述


原文地址:https://blog.csdn.net/weixin_44694682/article/details/142909740

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!