123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- <template>
- <div class="area-cropper">
- <div class="cropper-img">
- <img
- v-if="paper.imgUrl"
- ref="imgDom"
- :src="paper.imgUrl"
- @load="imgLoad"
- />
- </div>
- <div
- class="cropper-areas"
- v-move-ele.prevent="{
- moveStart: ($event) => {
- boxMouseDown($event);
- },
- moveElement: (pos, $event) => {
- boxMove($event);
- },
- moveStop: () => {
- boxMoveStop();
- },
- }"
- >
- <area-item
- v-for="area in areas"
- :key="area.key"
- :data="area"
- :cur-element="curArea"
- @del-element="removeArea"
- @act-element="actCurArea"
- @resize-over="modifyArea"
- ></area-item>
- <!-- 拖动选框 -->
- <div
- v-if="selectionStyles.w"
- class="area-selection"
- :style="{
- width: selectionStyles.w + 'px',
- height: selectionStyles.h + 'px',
- top: selectionStyles.y + 'px',
- left: selectionStyles.x + 'px',
- }"
- ></div>
- </div>
- </div>
- </template>
- <script>
- import MoveEle from "./move-ele";
- import AreaItem from "./ElementItem.vue";
- import { randomCode } from "@/plugins/utils";
- export default {
- name: "area-cropper",
- components: { AreaItem },
- directives: { MoveEle },
- props: {
- paper: {
- type: Object,
- default() {
- return {
- imgUrl: "",
- areas: [],
- };
- },
- },
- },
- data() {
- return {
- areas: [],
- initArea: { id: null, i: 0, x: 0, y: 0, w: 0, h: 0, zIndex: 0 },
- curArea: {},
- selectionStartPos: { x: 0, y: 0 },
- selectionStyles: { x: 0, y: 0, w: 0, h: 0 },
- IS_SELECT_ACTION: false,
- imgDisplayWidth: 0,
- imgDisplayHeight: 0,
- };
- },
- mounted() {
- window.addEventListener("resize", this.windowResizeEvent);
- document.addEventListener("keydown", this.keyEvent);
- },
- methods: {
- getMax(arr) {
- return Math.max.apply(null, arr);
- },
- toFixed(num, precision = 2) {
- return num.toFixed(precision) * 1;
- },
- imgLoad() {
- this.transformPicConfig(this.paper.areas);
- this.$emit("paper-load");
- },
- windowResizeEvent() {
- const { clientWidth, clientHeight } = this.$refs.imgDom;
- const hRate = clientHeight / this.imgDisplayHeight;
- const wRate = clientWidth / this.imgDisplayWidth;
- this.areas = this.areas.map((area) => {
- return Object.assign({}, area, {
- key: `key-${randomCode()}`,
- x: area.x * wRate,
- y: area.y * hRate,
- w: area.w * wRate,
- h: area.h * hRate,
- });
- });
- this.imgDisplayWidth = clientWidth;
- this.imgDisplayHeight = clientHeight;
- },
- keyEvent(e) {
- if (
- e.code === "Delete" &&
- !e.ctrlKey &&
- !e.altKey &&
- !e.shiftKey &&
- !e.repeat
- ) {
- if (!this.curArea.id) return;
- e.preventDefault();
- this.removeArea(this.curArea);
- return;
- }
- },
- transformPicConfig(areas) {
- const { clientWidth, clientHeight } = this.$refs.imgDom;
- this.areas = areas.map((area, index) => {
- let narea = {
- id: `id-${randomCode()}`,
- key: `key-${randomCode()}`,
- zIndex: index + 99,
- };
- if (Object.keys(area).join("") === "i") {
- narea = {
- ...narea,
- x: 0,
- y: 0,
- w: clientWidth,
- h: clientHeight,
- };
- } else {
- narea = {
- ...narea,
- x: area.x * clientWidth,
- y: area.y * clientHeight,
- w: area.w * clientWidth,
- h: area.h * clientHeight,
- };
- }
- return narea;
- });
- this.imgDisplayWidth = clientWidth;
- this.imgDisplayHeight = clientHeight;
- },
- addArea(data) {
- let area = Object.assign({}, this.initArea, data);
- const maxZIndex = this.areas.length
- ? this.getMax(this.areas.map((elem) => elem.zIndex))
- : 0;
- area.id = `id-${randomCode()}`;
- area.key = `key-${randomCode()}`;
- area.zIndex = maxZIndex + 1;
- this.areas.push(area);
- this.actCurArea(area);
- this.emitChange();
- },
- modifyArea(area) {
- const pos = this.areas.findIndex((elem) => elem.id === area.id);
- this.areas.splice(pos, 1, area);
- this.actCurArea(area);
- this.emitChange();
- },
- removeArea(area) {
- const pos = this.areas.findIndex((elem) => elem.id === area.id);
- this.areas.splice(pos, 1);
- if (this.areas.length) this.actCurArea(this.areas[0]);
- this.emitChange();
- },
- actCurArea(area) {
- this.curArea = area;
- this.$emit("curarea-change", area);
- },
- curareaChange(area) {
- if (area.id !== this.curArea.id) this.curArea = {};
- },
- emitChange() {
- const { clientWidth, clientHeight } = this.$refs.imgDom;
- const areas = this.areas.map((item) => {
- return {
- id: item.id,
- x: this.toFixed(item.x / clientWidth, 4),
- y: this.toFixed(item.y / clientHeight),
- w: this.toFixed(item.w / clientWidth, 4),
- h: this.toFixed(item.h / clientHeight),
- // 是否覆盖整个页面,允许2px的误差
- isFull:
- item.x <= 2 &&
- item.y <= 2 &&
- item.w + 2 >= clientWidth &&
- item.h + 2 >= clientHeight,
- };
- });
- this.$emit("change", areas);
- },
- getOffsetInfo(dom, endParentClass = "cropper-areas") {
- let parentNode = dom;
- let parentNodeClass = parentNode.getAttribute("class") || "";
- let offsetTop = 0,
- offsetLeft = 0;
- while (!parentNodeClass.includes(endParentClass)) {
- if (parentNode.offsetParent) {
- offsetTop += parentNode.offsetTop;
- offsetLeft += parentNode.offsetLeft;
- parentNode = parentNode.offsetParent;
- } else {
- offsetTop += parentNode.clientTop;
- offsetLeft += parentNode.clientLeft;
- parentNode = parentNode.parentNode;
- }
- parentNodeClass = parentNode.getAttribute("class") || "";
- }
- return {
- offsetLeft,
- offsetTop,
- };
- },
- boxMouseDown($event) {
- const { offsetLeft: x, offsetTop: y } = this.getOffsetInfo($event.target);
- this.selectionStartPos.x = x + $event.offsetX;
- this.selectionStartPos.y = y + $event.offsetY;
- },
- boxMove($event) {
- const { offsetLeft: x, offsetTop: y } = this.getOffsetInfo($event.target);
- const selectionEndPos = {
- x: x + $event.offsetX,
- y: y + $event.offsetY,
- };
- const sPos = {
- x: Math.min(this.selectionStartPos.x, selectionEndPos.x),
- y: Math.min(this.selectionStartPos.y, selectionEndPos.y),
- };
- const ePos = {
- x: Math.max(this.selectionStartPos.x, selectionEndPos.x),
- y: Math.max(this.selectionStartPos.y, selectionEndPos.y),
- };
- this.selectionStyles = {
- ...sPos,
- w: ePos.x - sPos.x,
- h: ePos.y - sPos.y,
- };
- this.IS_SELECT_ACTION = true;
- },
- boxMoveStop() {
- if (
- this.IS_SELECT_ACTION &&
- this.selectionStyles.w > 20 &&
- this.selectionStyles.h > 20
- )
- this.addArea(this.selectionStyles);
- this.selectionStyles = { x: 0, y: 0, w: 0, h: 0 };
- this.selectionStartPos = { x: 0, y: 0 };
- this.IS_SELECT_ACTION = false;
- },
- },
- beforeDestroy() {
- window.removeEventListener("resize", this.windowResizeEvent);
- document.removeEventListener("keydown", this.keyEvent);
- },
- };
- </script>
|