Location>code7788 >text

Image hotspot. vue3+ts and vue3+js writeup (js didn't finish writing the data, perfect functionality)

Popularity:738 ℃/2024-08-08 09:48:58

Without further ado, the code.

vue3+ts

<!-- Hot Zone Components -->
<template>
    <el-dialog v-model="dialog_visible" append-to-body fullscreen @close="close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">editorial hot zone</div>
            </div>
        </template>
        <el-scrollbar class="content-scrollbar">
            <div class="pa-40 flex-row gap-40">
                <div class="left-content flex-1 pa-20">
                    <el-scrollbar class="img-scrollbar">
                        <div class="img-container">
                            <div ref="imgBoxRef" @="start_drag" @="move_drag" @="end_drag">
                                <el-image :src="hot_list.img" class="w img" @ @ @></el-image>
                                <div ref="areaRef" class="area" :style="init_drag_style"></div>
                                <div v-for="(item, index) in hot_list.hot" :key="index" class="area-box" :style="rect_style(item.drag_start, item.drag_end)" @="start_drag_area_box(index, $event)" @dblclick="dbl_drag_event(item, index)">
                                    <div class="del-btn" @="del_area_event(index)"><icon name="close"></icon></div>
                                    <div class="drag-btn" :data-index="index" @="start_drag_btn(index, $event)"></div>
                                    <div class="text">
                                        <div class="name">{{  }}</div>
                                        <div class="status" :class=" ? 'cr-primary' : 'cr-error'">{{  ? 'configured' : 'as yet unsettled' }}</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </el-scrollbar>
                </div>
                <div class="right-content flex-1 pa-20">
                    <div class="size-16 fw mb-10">Photo hotspot</div>
                    <div class="size-12 cr-9 mb-20">Boxed hot zone range,Double-click to set hot zone information</div>
                    <div class="flex-col gap-20 item">
                        <div v-for="(item, index) in hot_list.hot" :key="index" class="flex-row align-c gap-10">
                            <el-input v-model="" class="name" placeholder="name (of a thing)"></el-input>
                            <url-value v-model=""></url-value>
                            <icon name="del" size="20" @click="del_event(index)"></icon>
                        </div>
                    </div>
                </div>
            </div>
        </el-scrollbar>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" type="primary" @click="confirm_event">fulfillment</el-button>
            </span>
        </template>
    </el-dialog>
    <el-dialog v-model="hot_dialog_visible" width="560" append-to-body @close="hot_close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">Setting the hot zone</div>
            </div>
        </template>
        <div class="content">
            <el-form ref="formRef" :model="form" label-width="85px" class="pa-20 mt-16">
                <el-form-item label="Hot Zone Jump Links">
                    <url-value v-model=""></url-value>
                </el-form-item>
            </el-form>
        </div>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" @click="hot_close_event">abolish</el-button>
                <el-button class="plr-28 ptb-10" type="primary" @click="hot_confirm_event">recognize</el-button>
            </span>
        </template>
    </el-dialog>
    <el-button class="w" @click="dialog_visible = true"><icon name="add">editorial hot zone</icon></el-button>
</template>
<script lang="ts" setup>
import { cloneDeep } from 'lodash';
const app = getCurrentInstance();
/**
 * @description: hot zone
 * @param modelValue{Object} default value
 * @param dialog_visible {Boolean} pop-up display
 * @return {*} update:modelValue
 */
const props = defineProps({});
const modelValue = defineModel({ type: Object as PropType<hotData>, default: {} });
const dialog_visible = defineModel('visibleDialog', { type: Boolean, default: false });
const hot_list = ref<hotData>({
    img: '',
    hot: [],
});
const hot_list_index = ref(0);
watch(
    () => ,
    (val) => {
        hot_list.value = cloneDeep(val);
        (val);
    },
    { immediate: true, deep: true }
);

//#region Left Canvas -----------------------------------------------start
const imgBoxRef = ref<HTMLElement | null>(null);
const rect_start = ref<rectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const rect_end = ref<rectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const areaRef = ref<HTMLElement | null>(null);
const init_drag_style = ref('');
const drag_bool = ref(false);
const drag_box_bool = ref(false);
const drag_box_scale_bool = ref(false);
const start_drag = (event: MouseEvent) => {
    drag_bool.value = true;
    if (!) return;
    rect_start. = event.clientX - ().left;
    rect_start. = event.clientY - ().top;
    rect_start. = 0;
    rect_start. = 0;
};
const move_drag = (event: MouseEvent) => {
    if (drag_bool.value) {
        if (!) return;
        rect_end. = event.clientX - ().left;
        rect_end. = event.clientY - ().top;
        rect_end. = rect_end. - rect_start. > 0 ? rect_end. - rect_start. : 0;
        rect_end. = rect_end. - rect_start. > 0 ? rect_end. - rect_start. : 0;
        init_drag_style.value = `left: ${rect_start.}px;top: ${rect_start.}px;width: ${(rect_end., 1)}px;height: ${(rect_end., 1)}px;display: flex;`;
    }
};
const end_drag = (event: MouseEvent) => {
    drag_bool.value = false;
    if ()  = 'none';
    if (!) return;
    init_drag_style.value = ``;
    if (rect_end. > 16 && rect_end. > 16) {
        hot_list.({
            name: 'hot zone' + (hot_list. + 1),
            link: {},
            drag_start: cloneDeep(rect_start.value),
            drag_end: cloneDeep(rect_end.value),
            status: false,
        });
    }
    rect_end.value = { x: 0, y: 0, width: 0, height: 0 };
};
const area_box_point = ref({ x: 0, y: 0 });
// area-box
const dbl_drag_event = (item: hotListData, index: number) => {
    hot_dialog_visible.value = true;
     = ;
    hot_list_index.value = index;
};
const start_drag_area_box = (index: number, event: MouseEvent) => {
    hot_list_index.value = index;
    event.stopPropagation();
    drag_box_bool.value = true;
    let clone_drag_start = cloneDeep(hot_list.[hot_list_index.value].drag_start);
    let clone_drag_end = cloneDeep(hot_list.[hot_list_index.value].drag_end);
    // Record the original position
    area_box_point.value = {
        x: clone_drag_start.x - event.clientX,
        y: clone_drag_start.y - event.clientY,
    };

    // Husband element method does not trigger when child element drag and drop method is triggered
     = (areaBoxEvent) => {
        ();
        if (drag_box_bool.value) {
            if (!) return;
            const new_coordinate = {
                x:  + area_box_point.,
                y:  + area_box_point.,
            };
            // Upper Left Boundary Judgment
            if (new_coordinate.x < 0) {
                new_coordinate.x = 0;
            }
            if (new_coordinate.y < 0) {
                new_coordinate.y = 0;
            }
            // Lower Right Boundary Judgment
            if (new_coordinate.x + (clone_drag_end.width, 1) > ().width) {
                new_coordinate.x = ().width - (clone_drag_end.width, 1);
            }
            if (new_coordinate.y + (clone_drag_end.height, 1) > ().height) {
                new_coordinate.y = ().height - (clone_drag_end.height, 1);
            }
            hot_list.[hot_list_index.value].drag_start.x = new_coordinate.x;
            hot_list.[hot_list_index.value].drag_start.y = new_coordinate.y;
        }
    };
     = (areaBoxEvent) => {
        ();
        drag_box_bool.value = false;
    };
};
// drag-btn
const start_drag_btn = (index: number, event: MouseEvent) => {
    hot_list_index.value = index;
    event.stopPropagation();
    drag_box_scale_bool.value = true;
    let clone_drag_start = hot_list.[hot_list_index.value].drag_start;
    let clone_drag_end = hot_list.[hot_list_index.value].drag_end;
     = (dragBtnEvent) => {
        ();
        //Subtract the position of the mouse relative to the element from the mouse position to get the element position
        if (drag_box_scale_bool.value) {
            if (!) return;
            clone_drag_end.x =  - ().left;
            clone_drag_end.y =  - ().top;
            hot_list.[hot_list_index.value].drag_end = {
                x: clone_drag_end.x,
                y: clone_drag_end.y,
                width: clone_drag_end.x - clone_drag_start.x > 0 ? clone_drag_end.x - clone_drag_start.x : 0,
                height: clone_drag_end.y - clone_drag_start.y > 0 ? clone_drag_end.y - clone_drag_start.y : 0,
            };
        }
    };
     = (dragBtnEvent2) => {
        ();
        drag_box_scale_bool.value = false;
    };
};
const del_area_event = (index: number) => {
    hot_list.(index, 1);
};
const rect_style = computed(() => {
    return (start: rectCoords, end: rectCoords) => {
        return `left: ${}px;top: ${}px;width: ${(, 1)}px;height: ${(, 1)}px;display: flex;`;
    };
});
//#endregion Left Canvas -----------------------------------------------end

//#region Right Hot Zone Editor -----------------------------------------------start
const del_event = (index: number) => {
    hot_list.(index, 1);
};
//#endregion Right Hot Zone Edit -----------------------------------------------end

//#region Setting up hot zone popups -----------------------------------------------start
const hot_dialog_visible = ref(false);
const form = ref({
    link: {},
});
const hot_close_event = () => {
    hot_dialog_visible.value = false;
};
const hot_confirm_event = () => {
    hot_list.[hot_list_index.value].link = ;
    hot_close_event();
};
//#endregion Setting up hot zone popups -----------------------------------------------end

//#region Hot Zone Confirmation Cancel Callback -----------------------------------------------start
// Cancel Callbacks
const close_event = () => {
    dialog_visible.value = false;
};
// acknowledge a pullback
const confirm_event = () => {
     = hot_list.value;
    close_event();
};
//#endregion Hot Zone Confirmation Cancel Callback -----------------------------------------------end
</script>
<style lang="scss" scoped>
.content-scrollbar {
    height: calc(100vh - 13.8rem);
    margin: 0 -1.6rem;
    .left-content {
        .img-scrollbar {
            display: flex;
            justify-content: center;
            .img-container {
                max-width: 60rem;
                min-width: 30rem;
                height: calc(100vh - 25.8rem);
                position: relative;
                .img {
                    user-select: none;
                    cursor: crosshair;
                }
                .area {
                    position: absolute;
                    background: rgba(41, 128, 185, 0.3);
                    border: 1px dashed #34495e;
                    width: 0px;
                    height: 0px;
                    left: 0px;
                    top: 0px;
                    display: none;
                }
                .area-box {
                    position: absolute;
                    background: rgba(42, 148, 255, 0.25);
                    border: 1px dashed #8ec6ff;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    color: #1989fa;
                    font-size: 1.2rem;
                    cursor: move;
                    transition: transform 0.1s;
                    .del-btn {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        background: #1890ff;
                        color: #fff;
                        text-align: center;
                        border-radius: 0 0 0 0.3rem;
                        position: absolute;
                        right: 0.7rem;
                        top: 0.7rem;
                        transform: translate3d(50%, -50%, 0);
                        cursor: default;
                        width: 1.6rem;
                        height: 1.6rem;
                        line-height: 1.6rem;
                        z-index: 1;
                        i {
                            font-size: 0.9rem;
                        }
                    }
                    .drag-btn {
                        position: absolute;
                        width: 7px;
                        height: 7px;
                        background: transparent;
                        right: 0;
                        bottom: 0;
                        transform: translate3d(50%, 50%, 0);
                        cursor: nwse-resize;
                        z-index: 1;
                    }
                    .text {
                        overflow: hidden;
                        display: flex;
                        flex-wrap: wrap;
                        justify-content: center;
                        max-width: 100%;
                        max-height: 100%;
                        text-align: center;
                        align-items: center;
                        color: #fff;
                        font-size: 1.2rem;
                        .name {
                            color: #fff;
                            margin: 0 0.2rem;
                        }
                        .status {
                            margin: 0 0.2rem;
                        }
                    }
                }
            }
        }
    }
    .right-content {
        .item {
            max-width: 47.8rem;
            .name {
                width: 9.8rem;
            }
        }
    }
}
</style>

vue3+js writeup

<!-- Upload Component -->
<template>
    <el-dialog v-model="dialogVisible" width="1168" append-to-body fullscreen @close="close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">editorial hot zone</div>
            </div>
        </template>
        <el-scrollbar class="content-scrollbar">
            <div class="pa-40 flex-row gap-40">
                <div class="left-content flex-1 pa-20">
                    <el-scrollbar class="img-scrollbar">
                        <div class="img-container">
                            <div ref="imgBoxRef" @="start_drag" @="move_drag" @="end_drag">
                                <el-image :src="" class="w img" @ @ @></el-image>
                                <div ref="areaRef" class="area" :style="init_drag_style"></div>
                            </div>
                        </div>
                    </el-scrollbar>
                </div>
                <div class="right-content flex-1 pa-20">
                    <div class="size-16 fw mb-10">Photo hotspot</div>
                    <div class="size-12 cr-9 mb-20">Boxed hot zone range,Double-click to set hot zone information</div>
                    <div class="flex-col gap-20 item">
                        <div v-for="(item, index) in " :key="index" class="flex-row align-c gap-10">
                            <el-input v-model="" class="name" placeholder="name (of a thing)"></el-input>
                            <url-value v-model=""></url-value>
                            <icon name="del" size="20" @click="del_event(index)"></icon>
                        </div>
                    </div>
                </div>
            </div>
        </el-scrollbar>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" type="primary" @click="confirm_event">fulfillment</el-button>
            </span>
        </template>
    </el-dialog>
    <el-button class="w" @click="dialogVisible = true"><icon name="add">editorial hot zone</icon></el-button>
</template>
<script lang="ts" setup>
import { cloneDeep } from 'lodash';
const app = getCurrentInstance();
/**
 * @description: hotzone
 * @param modelValue{Object} default value
 * @param dialogVisible {Boolean} The dialog is displayed.
 * @param type{String} The link type is an empty array means no limitation, all available, pass over means the value passed is available.
 * @param placeholder{String} The hint text.
 * @return {*} update:modelValue
*/
const props = defineProps({});
const modelValue = defineModel({ type: Object as PropType<hotData>, default: {} });
const dialogVisible = defineModel('visibleDialog', { type: Boolean, default: false });

//#region Left Canvas -----------------------------------------------start
interface RectCoords {
    x: number;
    y: number;
    width: number;
    height: number;
}

interface drag {
    status: boolean;
    name: string;
    link: object;
}
const drag_list = ref<drag[]>([]);

const imgBoxRef = ref<HTMLElement | null>(null);
const rect_start = ref<RectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const rect_end = ref<RectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const areaRef = ref<HTMLElement | null>(null);
const init_drag_style = ref('');
const drag_bool = ref(false);
const drag_box_bool = ref(false);
const drag_box_scale_bool = ref(false);
const start_drag = (event: MouseEvent) => {
    drag_bool.value = true;
    if (!) return;
    rect_start. = event.clientX - ().left;
    rect_start. = event.clientY - ().top;
    rect_start. = 0;
    rect_start. = 0;
};
const move_drag = (event: MouseEvent) => {
    if (drag_bool.value) {
        if (!) return;
        rect_end. = event.clientX - ().left;
        rect_end. = event.clientY - ().top;
        rect_end. = rect_end. - rect_start. > 0 ? rect_end. - rect_start. : 0;
        rect_end. = rect_end. - rect_start. > 0 ? rect_end. - rect_start. : 0;
        init_drag_style.value = rect_style(rect_start.value, rect_end.value);
    }
};

const end_drag = (event: MouseEvent) => {
    drag_bool.value = false;
    if ()  = 'none';
    if (!) return;
    let clone_drag_start = cloneDeep(rect_start.value);
    let clone_drag_end = cloneDeep(rect_end.value);
    init_drag_style.value = ``;
    if (rect_end. > 16 && rect_end. > 16) {
        // Clone the area element and change class="area" to area-box
        const area_box = ('div');
        area_box.className = 'area-box';
        area_box. = rect_style(clone_drag_start, clone_drag_end);
        if () ?.appendChild(area_box);
        drag_list.({
            name: 'hot zone' + drag_list. + 1,
            link: {},
            status: false,
        });
        // area_box Add drag-and-drop functionality
        area_box.onmousedown = (areaEvent) => {
            ();
            drag_box_bool.value = true;
            // Record the original position
            let area_box_point = {
                x: clone_drag_start.x - ,
                y: clone_drag_start.y - ,
            };
            // Husband element method does not trigger when child element drag and drop method is triggered
             = (areaBoxEvent) => {
                ();
                if (drag_box_bool.value) {
                    ('area_box onmousemove');
                    if (!) return;
                    const new_coordinate = {
                        x:  + area_box_point.x,
                        y:  + area_box_point.y,
                    };
                    // Upper Left Boundary Judgment
                    if (new_coordinate.x < 0) {
                        new_coordinate.x = 0;
                    }
                    if (new_coordinate.y < 0) {
                        new_coordinate.y = 0;
                    }
                    // Lower Right Boundary Judgment
                    if (new_coordinate.x + (clone_drag_end.width, 1) > ().width) {
                        new_coordinate.x = ().width - (clone_drag_end.width, 1);
                    }
                    if (new_coordinate.y + (clone_drag_end.height, 1) > ().height) {
                        new_coordinate.y = ().height - (clone_drag_end.height, 1);
                    }
                    clone_drag_start.x = new_coordinate.x;
                    clone_drag_start.y = new_coordinate.y;
                    area_box. = rect_style(clone_drag_start, clone_drag_end);
                }
            };
             = (areaBoxEvent) => {
                ();
                drag_box_bool.value = false;
            };
        };
        // Creating a delete button within an added element ---------------
        const del_btn = ('div');
        del_btn.className = 'del-btn';
        del_btn.innerHTML = `<i class="iconfont icon-close"></i>`;
        area_box.appendChild(del_btn);
        del_btn.onclick = () => {
            event.stopPropagation();
            // Completely remove the parent area_box of the currently clicked del_btn without leaving any traces.
            area_box.parentNode?.removeChild(area_box);
        };
        // Creating drag and drop buttons within added elements ---------------
        const drag_btn = ('div');
        drag_btn.className = 'drag-btn';
        area_box.appendChild(drag_btn);
        // Husband element method does not trigger when child element drag and drop method is triggered
        drag_btn.onmousedown = (dragBtnEvent) => {
            ();
            drag_box_scale_bool.value = true;
            // // Drag events bound to document: If the mouse drags too fast when bound to the element itself, the mouse will leave the dragged element, causing the drag to be invalidated after a certain distance.
             = (dragBtnEvent2) => {
                ();
                //Subtract the position of the mouse relative to the element from the mouse position to get the element position
                if (drag_box_scale_bool.value) {
                    if (!) return;
                    clone_drag_end.x =  - ().left;
                    clone_drag_end.y =  - ().top;
                    clone_drag_end.width = clone_drag_end.x - clone_drag_start.x > 0 ? clone_drag_end.x - clone_drag_start.x : 0;
                    clone_drag_end.height = clone_drag_end.y - clone_drag_start.y > 0 ? clone_drag_end.y - clone_drag_start.y : 0;
                    area_box. = rect_style(clone_drag_start, clone_drag_end);
                }
            };
             = (dragBtnEvent3) => {
                ();
                drag_box_scale_bool.value = false;
            };
        };
        // Create text alerts within added elements ---------------
        const drag_text = ('div');
        drag_text.className = 'text';
        drag_text.innerHTML = `<div class="name">Hot Zone I</div><div class="status">as yet unsettled</div>`;
        area_box.appendChild(drag_text);
    }
    rect_end.value = { x: 0, y: 0, width: 0, height: 0 };
};
const rect_style = (start: RectCoords, end: RectCoords) => {
    return `left: ${}px;top: ${}px;width: ${(, 1)}px;height: ${(, 1)}px;display: flex;`;
};
//#region Left Canvas -----------------------------------------------end

//#region Right Hot Zone Editor -----------------------------------------------start
const del_event = (index: number) => {
    (index, 1);
};
//#region Right Hot Zone Editor -----------------------------------------------end

//#region Hot Zone Confirmation Cancel Callback -----------------------------------------------start
// Cancel Callbacks
const close_event = () => {
     = false;
};
// acknowledge a pullback
const confirm_event = () => {
    //  = link_value.value;
    close_event();
};
//#endregion Hot Zone Confirmation Cancel Callback -----------------------------------------------end
</script>
<style lang="scss" scoped>
.content-scrollbar {
    height: calc(100vh - 13.8rem);
    margin: 0 -1.6rem;
    .left-content {
        .img-scrollbar {
            display: flex;
            justify-content: center;
            .img-container {
                max-width: 60rem;
                min-width: 30rem;
                height: calc(100vh - 25.8rem);
                position: relative;
                .img {
                    user-select: none;
                    cursor: crosshair;
                }
                .area {
                    position: absolute;
                    background: rgba(41, 128, 185, 0.3);
                    border: 1px dashed #34495e;
                    width: 0px;
                    height: 0px;
                    left: 0px;
                    top: 0px;
                    display: none;
                }
                :deep(.area-box) {
                    position: absolute;
                    background: rgba(42, 148, 255, 0.25);
                    border: 1px dashed #8ec6ff;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    color: #1989fa;
                    font-size: 1.2rem;
                    cursor: move;
                    transition: transform 0.1s;
                    .del-btn {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        background: #1890ff;
                        color: #fff;
                        text-align: center;
                        border-radius: 0 0 0 0.3rem;
                        position: absolute;
                        right: 0.7rem;
                        top: 0.7rem;
                        transform: translate3d(50%, -50%, 0);
                        cursor: default;
                        width: 1.6rem;
                        height: 1.6rem;
                        line-height: 1.6rem;
                        z-index: 1;
                        i {
                            font-size: 0.9rem;
                        }
                    }
                    .drag-btn {
                        position: absolute;
                        width: 7px;
                        height: 7px;
                        background: transparent;
                        right: 0;
                        bottom: 0;
                        transform: translate3d(50%, 50%, 0);
                        cursor: nwse-resize;
                        z-index: 1;
                    }
                    .text {
                        overflow: hidden;
                        display: flex;
                        flex-wrap: wrap;
                        justify-content: center;
                        max-width: 100%;
                        max-height: 100%;
                        text-align: center;
                        align-items: center;
                        color: #fff;
                        font-size: 1.2rem;
                        .name,
                        .status {
                            color: #fff;
                            margin: 0 2px;
                        }
                    }
                }
            }
        }
    }
    .right-content {
        .item {
            max-width: 47.8rem;
            .name {
                width: 9.8rem;
            }
        }
    }
}
</style>

Preview effect: