纯前端实现用户在线制作操作引导(图片区域画框并且可引导)intro.js

 前言

最近接了一个需求,要求:由于页面太多,实施自己在图片上进行画框然后实现操作引导.这个需求分为两步:

1.用户需要在页面上先上传指定大小图片

2.用户在图片内绘制区域,并输入操作引导的引导语

3.绘制好后保存到数据库中

4.在项目中按需进行查询指定的引导即可

引入相关依赖配置

1.intor.js

大家可以看我之前的文章是如何下载依赖引入的传送门→前端使用intro.js实现页面操作引导

话不多说开始操作↓

详细步骤

这里我是用html文件写的,大家换成vue直接按需粘过去就行

需要更改的地方:

1.上传图片的地址连接,换成自己公司的地址

2.样式可以自行更改~

详细步骤备注我就写在代码注释里面啦

<!DOCTYPE html>
<html lang="zh_CN">
<head th:include="include :: header">
    <title>引导设置</title>
    <!--  引入操作引导样式组件  -->
    <link rel="stylesheet" th:href="@{/css/introjs.css}">
</head>
<style>
    #big_Img {
        position: relative;
        /*使大图随着小图移动的定位*/
        z-index: 2;
    }

    #big_Img .requ {
        border: 2px rgba(255, 27, 27, 0.76) dashed;
        position: absolute;
        cursor: move;
        resize: both;
        /*使绘制的div可以拖动改变大小的样式 */
        overflow: auto;
        z-index: 999;
    }

    .drag {
        border: 1px #5da8ff solid;
        width: 50px;
        height: 24px;
        background: #fff;
        cursor: pointer;
        margin: auto;
        margin-top: 8px;
    }

    .drag:active {
        box-shadow: 3px 3px 10px 0 #111111;
        width: 51px;
        height: 25px;
    }

    #big_Img {
        border: 1px solid #ccc;
        width: 900px;
        height: 450px;
    }

    #img {
        pointer-events: none;
        user-select: none;
        -webkit-user-drag: none;
        width: 900px;
        height: 450px;
        margin: auto;
        position: relative;
    }

    .guanbi {
        position: absolute;
        left: 0px;
        bottom: 0px;
        height: 21px;
        width: 20px;
        border-radius: 50%;
        background-color: #0f0f0f;
        color: #fff;
        text-align: center;
        line-height: 20px;
        cursor: pointer;
    }
    .el-form-item{
        width: 380px !important;
    }
    .el-form-item__content {
        width: 200px !important;
    }
    .el-form-item__label{
        width: 140px !important;
    }
</style>
<body>
<div id="main" style="display: flex;padding: 20px;" v-cloak>
    <div style="width: 70%;">
        <el-form label-width="160px" inline>
            <el-form-item label="选择菜单">
                <el-select v-model="menu" clearable placeholder="请选择菜单">
                    <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
                    </el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="选择对应功能">
                <el-select v-model="Feature" clearable placeholder="请选择对应功能">
                    <el-option v-for="item in featureData" :key="item.value" :label="item.label"
                               :value="item.value">
                    </el-option>
                </el-select>
            </el-form-item>
            <el-button type="primary" @click="dialogVisible = true">添加功能</el-button>
            <el-button type="primary" @click="removeMenu">删除功能</el-button>
            <br>
            <el-form-item label="引导语">
                <el-input v-model="yindaoyu" style="width: 200px;" clearable></el-input>
            </el-form-item>
            <el-form-item label="请拖拽右侧至图片区域">
                <div draggable="true" id="source" class="drag"></div>
            </el-form-item>
            <el-button @click="caozuo()">预览效果</el-button>
            <el-button type="primary" @click="save">保存</el-button>
        </el-form>
        <h3 style="color: #000;font-size: 20px">图片区域</h3>
        <div id="big_Img" class="v2" draggable="true">
            <div id="img"></div>
        </div>
    </div>
    <div style="height: 148px">
        <el-upload
                v-model="dialogImageUrl"
                action="/wyservice/workquery/uploadImg"
                list-type="picture-card"
                :on-preview="handlePictureCardPreview"
                :limit="1"
                :on-exceed="handleExceed"
                :on-success="((response, file, fileList)=>handleSuccess(response, file, fileList,'imgs'))"
                :on-remove="handleRemove">
            <div style="text-align: center"><img style="margin-bottom: 20px"  src="/img/lushang/upload_pictures.png"/>
                <p style="margin-top: -110px;">点击上传图片</p></div>
        </el-upload>
        <el-dialog :visible.sync="dialogVisible1">
            <img width="100%" :src="dialogImageUrl" alt="">
        </el-dialog>
    </div>

    <!-- 添加框 -->
    <el-dialog title="添加功能按钮" :visible.sync="dialogVisible" width="30%">
        <el-form :model="formInline" class="demo-form-inline" label-width="160px">
            <el-form-item label="选择菜单">
                <el-select v-model="formInline.formPage" clearable placeholder="请选择菜单">
                    <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
                    </el-option>
                </el-select>
            </el-form-item>
            <el-form-item label="输入功能标题">
                <el-input v-model="formInline.featureTitle" clearable></el-input>
            </el-form-item>
            <el-form-item label="输入功能描述">
                <el-input v-model="formInline.featureContent" clearable></el-input>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="btnAdd">确定</el-button>
            </el-form-item>
        </el-form>
    </el-dialog>
</div>

<div th:include="include :: footer"></div>
<div th:include="include::load"></div>
<!-- 引入操作引导组件 -->
<script th:src="@{/js/intro/intro.js}"></script>
<script th:src="@{/js/intro/introComponent.js}"></script>
<script th:src="@{/js/appjs/operationGuidance/operationGuidance.js}"></script>
</body>
</html>
let vm = new Vue({
    el: '#main',
    data: {
        dialogImageUrl: 'https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF',
        dialogVisible1: false,
        dialogVisible: false,
        yindaoyu: '',
        formInline: {
            formPage: '',
            featureTitle: '',
            featureContent: '',
        },
        menu: '',
        Feature: '',
        pageData: [],
        featureData: [],


        tag_number: 0,
        mobileDiv: false,
        move_x: '',
        move_y: '',
        lt: '',
        ls: '',
    },
    mounted() {
        //拖拽元素绘制div
        source.ondragend = ev => {
            if (this.yindaoyu) {
                this.draw(ev)
            } else {
                layer.msg("请先输入引导语")
            }
        }
    },
    methods: {
        //确定添加
        btnAdd() {
            this.dialogVisible = false
        },
        //删除功能
        removeMenu(){

        },
        //点击保存
        save() {
            let htmlCode = document.getElementById('big_Img').outerHTML;
            console.log(htmlCode);
        },
        draw(ev) {
            var oBox = document.getElementById("big_Img");
            ev = window.event || ev;
            var move_x1, move_y1, move_x2, move_y2, move_x3, move_y3, move_x4, move_y4;
            var isMove = false;
            var angle_x;
            var x1 = ev.clientX - 130 - oBox.offsetLeft; //起始坐标,130是左边导航的宽度
            var y1 = ev.clientY - 30 - oBox.offsetTop;
            var x2 = x1 + 100;
            var y2 = y1;
            var x4 = x1;
            var y4 = y1 + 100;
            var x3 = ev.clientX - 80 - oBox.offsetLeft + 50; //终点坐标
            var y3 = ev.clientY + 20 - oBox.offsetTop + 50;
            var degree = 0;
            var oDiv = document.createElement("div");
            // var otext = document.createElement("div1");
            // oBox.appendChild(otext);
            oBox.appendChild(oDiv);
            // otext.style.left = x1 - 15 + "px"; //在绘制的大div左上角的位置
            // otext.style.top = y1 - 15 + "px";
            //15是要减去小div的大小,这样位置才会在div的外面
            this.tag_number++;
            // otext.id = this.tag_number;
            // otext.innerText = this.tag_number;
            //在绘制的div1中加上文本,这里是加上左上角的编号
            oDiv.style.left = (x3 > x1 ? x1 : x3) + "px"; //绘制oDiv的样式
            oDiv.style.top = (y3 > y1 ? y1 : y3) + "px";
            oDiv.style.width = Math.abs(x3 - x1) + "px";
            oDiv.style.height = Math.abs(y3 - y1) + "px";
            oDiv.id = this.tag_number
            oDiv.setAttribute("class", "requ");
            oDiv.setAttribute("data-step", this.tag_number); //添加指引步骤从0开始,如果用户删除中间的,也不会影响
            oDiv.setAttribute("data-intro", this.yindaoyu); //这里是用户输入的引导语
            oDiv.innerHTML = "<div class=" + "guanbi" + ">x<div>" //关闭按钮

            let guan = oDiv.children[0]
            guan.onclick = ev => {  //鼠标点击
                oDiv.remove()
            }

            oDiv.onmousedown = ev => {
                var down_x1 = ev.clientX; //获取鼠标按下的坐标
                var down_y1 = ev.clientY;
                //获取元素的left,top值
                var l = oDiv.offsetLeft;
                var t = oDiv.offsetTop;
                if (ev.offsetX < oDiv.clientWidth - 10 && ev.offsetY < oDiv.clientHeight - 10) {  //判断是点击的右下角改变大小还是移动div
                    this.mobileDiv = true   //移动div
                } else {
                    this.mobileDiv = false   //改变大小
                }
                document.onmousemove = ev => {   //鼠标移动事件
                    var ev = ev || event;
                    //获取鼠标移动时的坐标
                    var down_x2 = ev.clientX;
                    var down_y2 = ev.clientY;
                    //计算出鼠标的移动距离
                    this.move_x = down_x2 - down_x1;
                    this.move_y = down_y2 - down_y1;
                    //移动的数值与元素的left,top相加,得出元素的移动的距离
                    this.lt = this.move_y + t;
                    this.ls = this.move_x + l;
                    if (this.mobileDiv == true) { //移动div
                        //更改元素的left,top值
                        oDiv.style.top = this.lt + 'px';
                        oDiv.style.left = this.ls + 'px';
                    } else {
                        //改变div大小开始
                        //获取鼠标移动时的坐标
                        var down_x2 = ev.clientX;
                        var down_y2 = ev.clientY;
                    }
                }
                document.onmouseup = ev => {  //鼠标抬起
                    document.onmousemove = null;
                    if (this.mobileDiv == true) {
                        // otext.style.top = this.lt - 15 + 'px'; //随着大div的移动而移动
                        // otext.style.left = this.ls - 15 + 'px';
                    } else {
                        //改变大小的鼠标抬起
                    }
                }
            }
        },
        caozuo() {
            introJs('.v2').setOptions({
                nextLabel: "下一步", // 下一个的按钮文字
                prevLabel: "上一步", // 上一个按钮文字
                //   skipLabel: "跳过", // 跳过指引的按钮文字
                doneLabel: "完成", // 完成按钮的文字
                hidePrev: false, // 是否在第一步中隐藏“上一步”按钮;不隐藏,将呈现为一个禁用的按钮
                hideNext: false, // 是否在最后一步中隐藏“下一步”按钮(同时会隐藏完成按钮);不隐藏,将呈现为一个禁用的按钮
                exitOnEsc: true, // 点击键盘的ESC按钮是否退出指引
                exitOnOverlayClick: false, // 点击遮罩层时是否退出介绍
                showStepNumbers: false, // 是否显示步骤编号
                disableInteraction: true, // 是否禁用高亮显示框内元素的交互
                showBullets: true, // 是否显示面板的指示点
                overlayOpacity: 0.7, // 遮罩层的透明度 0-1之间
                helperElementPadding: 10, // 选中的指引元素周围的填充距离
                showProgress: true, //进度条
            }).start();
        },

        handleRemove(file, fileList) {
            let url = this.dialogImageUrl.split('http://geecity-image.oss-cn-qingdao.aliyuncs.com/')
            $.ajax({
                type: "POST",
                url: "/workquery/gdlist/delimg",
                data: {filename: url[1]},
                error: function (request) {
                    parent.layer.alert("Connection error");
                },
                success: (data) => {
                    this.dialogImageUrl = ''
                    $("#img").css({
                        "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
                    });
                    layer.msg("删除成功")
                }
            })
        },
        handlePictureCardPreview(file) {
            this.dialogImageUrl = file.url;
            this.dialogVisible1 = true;
        },
        handleSuccess(response, file, fileList, type) {
            this.dialogImageUrl = response.data.src
            $("#img").css({
                "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
                "background-size": "950px"
            });
        },
        handleExceed() {
            return layer.msg("最多上传1张图片")
        }
    },
})

introComponent文件

function intro_lq(yindaoList) {
    let setOptions = {
        nextLabel: "下一步", // 下一个的按钮文字
        prevLabel: "上一步", // 上一个按钮文字
        //   skipLabel: "跳过", // 跳过指引的按钮文字
        doneLabel: "完成", // 完成按钮的文字
        hidePrev: false, // 是否在第一步中隐藏“上一步”按钮;不隐藏,将呈现为一个禁用的按钮
        hideNext: false, // 是否在最后一步中隐藏“下一步”按钮(同时会隐藏完成按钮);不隐藏,将呈现为一个禁用的按钮
        exitOnEsc: true, // 点击键盘的ESC按钮是否退出指引
        exitOnOverlayClick: false, // 点击遮罩层时是否退出介绍
        showStepNumbers: false, // 是否显示步骤编号
        disableInteraction: true, // 是否禁用高亮显示框内元素的交互
        showBullets: true, // 是否显示面板的指示点
        overlayOpacity: 0.7, // 遮罩层的透明度 0-1之间
        helperElementPadding: 10, // 选中的指引元素周围的填充距离
        showProgress: true, //进度条
        steps: yindaoList
    }
    return setOptions
}

结束

这里面代码都很详细啦,大家看一遍就懂了,如果不懂可以评论哦,回复超快

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AKA多个A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值