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


     前言

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

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

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

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

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

    引入相关依赖配置

    1.intor.js

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

    话不多说开始操作↓

    详细步骤

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

    需要更改的地方:

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

    2.样式可以自行更改~

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

    1. html>
    2. <html lang="zh_CN">
    3. <head th:include="include :: header">
    4. <title>引导设置title>
    5. <link rel="stylesheet" th:href="@{/css/introjs.css}">
    6. head>
    7. <style>
    8. #big_Img {
    9. position: relative;
    10. /*使大图随着小图移动的定位*/
    11. z-index: 2;
    12. }
    13. #big_Img .requ {
    14. border: 2px rgba(255, 27, 27, 0.76) dashed;
    15. position: absolute;
    16. cursor: move;
    17. resize: both;
    18. /*使绘制的div可以拖动改变大小的样式 */
    19. overflow: auto;
    20. z-index: 999;
    21. }
    22. .drag {
    23. border: 1px #5da8ff solid;
    24. width: 50px;
    25. height: 24px;
    26. background: #fff;
    27. cursor: pointer;
    28. margin: auto;
    29. margin-top: 8px;
    30. }
    31. .drag:active {
    32. box-shadow: 3px 3px 10px 0 #111111;
    33. width: 51px;
    34. height: 25px;
    35. }
    36. #big_Img {
    37. border: 1px solid #ccc;
    38. width: 900px;
    39. height: 450px;
    40. }
    41. #img {
    42. pointer-events: none;
    43. user-select: none;
    44. -webkit-user-drag: none;
    45. width: 900px;
    46. height: 450px;
    47. margin: auto;
    48. position: relative;
    49. }
    50. .guanbi {
    51. position: absolute;
    52. left: 0px;
    53. bottom: 0px;
    54. height: 21px;
    55. width: 20px;
    56. border-radius: 50%;
    57. background-color: #0f0f0f;
    58. color: #fff;
    59. text-align: center;
    60. line-height: 20px;
    61. cursor: pointer;
    62. }
    63. .el-form-item{
    64. width: 380px !important;
    65. }
    66. .el-form-item__content {
    67. width: 200px !important;
    68. }
    69. .el-form-item__label{
    70. width: 140px !important;
    71. }
    72. style>
    73. <body>
    74. <div id="main" style="display: flex;padding: 20px;" v-cloak>
    75. <div style="width: 70%;">
    76. <el-form label-width="160px" inline>
    77. <el-form-item label="选择菜单">
    78. <el-select v-model="menu" clearable placeholder="请选择菜单">
    79. <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
    80. el-option>
    81. el-select>
    82. el-form-item>
    83. <el-form-item label="选择对应功能">
    84. <el-select v-model="Feature" clearable placeholder="请选择对应功能">
    85. <el-option v-for="item in featureData" :key="item.value" :label="item.label"
    86. :value="item.value">
    87. el-option>
    88. el-select>
    89. el-form-item>
    90. <el-button type="primary" @click="dialogVisible = true">添加功能el-button>
    91. <el-button type="primary" @click="removeMenu">删除功能el-button>
    92. <br>
    93. <el-form-item label="引导语">
    94. <el-input v-model="yindaoyu" style="width: 200px;" clearable>el-input>
    95. el-form-item>
    96. <el-form-item label="请拖拽右侧至图片区域">
    97. <div draggable="true" id="source" class="drag">div>
    98. el-form-item>
    99. <el-button @click="caozuo()">预览效果el-button>
    100. <el-button type="primary" @click="save">保存el-button>
    101. el-form>
    102. <h3 style="color: #000;font-size: 20px">图片区域h3>
    103. <div id="big_Img" class="v2" draggable="true">
    104. <div id="img">div>
    105. div>
    106. div>
    107. <div style="height: 148px">
    108. <el-upload
    109. v-model="dialogImageUrl"
    110. action="/wyservice/workquery/uploadImg"
    111. list-type="picture-card"
    112. :on-preview="handlePictureCardPreview"
    113. :limit="1"
    114. :on-exceed="handleExceed"
    115. :on-success="((response, file, fileList)=>handleSuccess(response, file, fileList,'imgs'))"
    116. :on-remove="handleRemove">
    117. <div style="text-align: center"><img style="margin-bottom: 20px" src="/img/lushang/upload_pictures.png"/>
    118. <p style="margin-top: -110px;">点击上传图片p>div>
    119. el-upload>
    120. <el-dialog :visible.sync="dialogVisible1">
    121. <img width="100%" :src="dialogImageUrl" alt="">
    122. el-dialog>
    123. div>
    124. <el-dialog title="添加功能按钮" :visible.sync="dialogVisible" width="30%">
    125. <el-form :model="formInline" class="demo-form-inline" label-width="160px">
    126. <el-form-item label="选择菜单">
    127. <el-select v-model="formInline.formPage" clearable placeholder="请选择菜单">
    128. <el-option v-for="item in pageData" :key="item.value" :label="item.label" :value="item.value">
    129. el-option>
    130. el-select>
    131. el-form-item>
    132. <el-form-item label="输入功能标题">
    133. <el-input v-model="formInline.featureTitle" clearable>el-input>
    134. el-form-item>
    135. <el-form-item label="输入功能描述">
    136. <el-input v-model="formInline.featureContent" clearable>el-input>
    137. el-form-item>
    138. <el-form-item>
    139. <el-button type="primary" @click="btnAdd">确定el-button>
    140. el-form-item>
    141. el-form>
    142. el-dialog>
    143. div>
    144. <div th:include="include :: footer">div>
    145. <div th:include="include::load">div>
    146. <script th:src="@{/js/intro/intro.js}">script>
    147. <script th:src="@{/js/intro/introComponent.js}">script>
    148. <script th:src="@{/js/appjs/operationGuidance/operationGuidance.js}">script>
    149. body>
    150. html>
    1. let vm = new Vue({
    2. el: '#main',
    3. data: {
    4. dialogImageUrl: 'https://t7.baidu.com/it/u=4198287529,2774471735&fm=193&f=GIF',
    5. dialogVisible1: false,
    6. dialogVisible: false,
    7. yindaoyu: '',
    8. formInline: {
    9. formPage: '',
    10. featureTitle: '',
    11. featureContent: '',
    12. },
    13. menu: '',
    14. Feature: '',
    15. pageData: [],
    16. featureData: [],
    17. tag_number: 0,
    18. mobileDiv: false,
    19. move_x: '',
    20. move_y: '',
    21. lt: '',
    22. ls: '',
    23. },
    24. mounted() {
    25. //拖拽元素绘制div
    26. source.ondragend = ev => {
    27. if (this.yindaoyu) {
    28. this.draw(ev)
    29. } else {
    30. layer.msg("请先输入引导语")
    31. }
    32. }
    33. },
    34. methods: {
    35. //确定添加
    36. btnAdd() {
    37. this.dialogVisible = false
    38. },
    39. //删除功能
    40. removeMenu(){
    41. },
    42. //点击保存
    43. save() {
    44. let htmlCode = document.getElementById('big_Img').outerHTML;
    45. console.log(htmlCode);
    46. },
    47. draw(ev) {
    48. var oBox = document.getElementById("big_Img");
    49. ev = window.event || ev;
    50. var move_x1, move_y1, move_x2, move_y2, move_x3, move_y3, move_x4, move_y4;
    51. var isMove = false;
    52. var angle_x;
    53. var x1 = ev.clientX - 130 - oBox.offsetLeft; //起始坐标,130是左边导航的宽度
    54. var y1 = ev.clientY - 30 - oBox.offsetTop;
    55. var x2 = x1 + 100;
    56. var y2 = y1;
    57. var x4 = x1;
    58. var y4 = y1 + 100;
    59. var x3 = ev.clientX - 80 - oBox.offsetLeft + 50; //终点坐标
    60. var y3 = ev.clientY + 20 - oBox.offsetTop + 50;
    61. var degree = 0;
    62. var oDiv = document.createElement("div");
    63. // var otext = document.createElement("div1");
    64. // oBox.appendChild(otext);
    65. oBox.appendChild(oDiv);
    66. // otext.style.left = x1 - 15 + "px"; //在绘制的大div左上角的位置
    67. // otext.style.top = y1 - 15 + "px";
    68. //15是要减去小div的大小,这样位置才会在div的外面
    69. this.tag_number++;
    70. // otext.id = this.tag_number;
    71. // otext.innerText = this.tag_number;
    72. //在绘制的div1中加上文本,这里是加上左上角的编号
    73. oDiv.style.left = (x3 > x1 ? x1 : x3) + "px"; //绘制oDiv的样式
    74. oDiv.style.top = (y3 > y1 ? y1 : y3) + "px";
    75. oDiv.style.width = Math.abs(x3 - x1) + "px";
    76. oDiv.style.height = Math.abs(y3 - y1) + "px";
    77. oDiv.id = this.tag_number
    78. oDiv.setAttribute("class", "requ");
    79. oDiv.setAttribute("data-step", this.tag_number); //添加指引步骤从0开始,如果用户删除中间的,也不会影响
    80. oDiv.setAttribute("data-intro", this.yindaoyu); //这里是用户输入的引导语
    81. oDiv.innerHTML = "
      "guanbi" + ">x
      " //关闭按钮
    82. let guan = oDiv.children[0]
    83. guan.onclick = ev => { //鼠标点击
    84. oDiv.remove()
    85. }
    86. oDiv.onmousedown = ev => {
    87. var down_x1 = ev.clientX; //获取鼠标按下的坐标
    88. var down_y1 = ev.clientY;
    89. //获取元素的left,top值
    90. var l = oDiv.offsetLeft;
    91. var t = oDiv.offsetTop;
    92. if (ev.offsetX < oDiv.clientWidth - 10 && ev.offsetY < oDiv.clientHeight - 10) { //判断是点击的右下角改变大小还是移动div
    93. this.mobileDiv = true //移动div
    94. } else {
    95. this.mobileDiv = false //改变大小
    96. }
    97. document.onmousemove = ev => { //鼠标移动事件
    98. var ev = ev || event;
    99. //获取鼠标移动时的坐标
    100. var down_x2 = ev.clientX;
    101. var down_y2 = ev.clientY;
    102. //计算出鼠标的移动距离
    103. this.move_x = down_x2 - down_x1;
    104. this.move_y = down_y2 - down_y1;
    105. //移动的数值与元素的left,top相加,得出元素的移动的距离
    106. this.lt = this.move_y + t;
    107. this.ls = this.move_x + l;
    108. if (this.mobileDiv == true) { //移动div
    109. //更改元素的left,top值
    110. oDiv.style.top = this.lt + 'px';
    111. oDiv.style.left = this.ls + 'px';
    112. } else {
    113. //改变div大小开始
    114. //获取鼠标移动时的坐标
    115. var down_x2 = ev.clientX;
    116. var down_y2 = ev.clientY;
    117. }
    118. }
    119. document.onmouseup = ev => { //鼠标抬起
    120. document.onmousemove = null;
    121. if (this.mobileDiv == true) {
    122. // otext.style.top = this.lt - 15 + 'px'; //随着大div的移动而移动
    123. // otext.style.left = this.ls - 15 + 'px';
    124. } else {
    125. //改变大小的鼠标抬起
    126. }
    127. }
    128. }
    129. },
    130. caozuo() {
    131. introJs('.v2').setOptions({
    132. nextLabel: "下一步", // 下一个的按钮文字
    133. prevLabel: "上一步", // 上一个按钮文字
    134. // skipLabel: "跳过", // 跳过指引的按钮文字
    135. doneLabel: "完成", // 完成按钮的文字
    136. hidePrev: false, // 是否在第一步中隐藏“上一步”按钮;不隐藏,将呈现为一个禁用的按钮
    137. hideNext: false, // 是否在最后一步中隐藏“下一步”按钮(同时会隐藏完成按钮);不隐藏,将呈现为一个禁用的按钮
    138. exitOnEsc: true, // 点击键盘的ESC按钮是否退出指引
    139. exitOnOverlayClick: false, // 点击遮罩层时是否退出介绍
    140. showStepNumbers: false, // 是否显示步骤编号
    141. disableInteraction: true, // 是否禁用高亮显示框内元素的交互
    142. showBullets: true, // 是否显示面板的指示点
    143. overlayOpacity: 0.7, // 遮罩层的透明度 0-1之间
    144. helperElementPadding: 10, // 选中的指引元素周围的填充距离
    145. showProgress: true, //进度条
    146. }).start();
    147. },
    148. handleRemove(file, fileList) {
    149. let url = this.dialogImageUrl.split('http://geecity-image.oss-cn-qingdao.aliyuncs.com/')
    150. $.ajax({
    151. type: "POST",
    152. url: "/workquery/gdlist/delimg",
    153. data: {filename: url[1]},
    154. error: function (request) {
    155. parent.layer.alert("Connection error");
    156. },
    157. success: (data) => {
    158. this.dialogImageUrl = ''
    159. $("#img").css({
    160. "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
    161. });
    162. layer.msg("删除成功")
    163. }
    164. })
    165. },
    166. handlePictureCardPreview(file) {
    167. this.dialogImageUrl = file.url;
    168. this.dialogVisible1 = true;
    169. },
    170. handleSuccess(response, file, fileList, type) {
    171. this.dialogImageUrl = response.data.src
    172. $("#img").css({
    173. "background": "url("+this.dialogImageUrl+")" + ""+ "no-repeat",
    174. "background-size": "950px"
    175. });
    176. },
    177. handleExceed() {
    178. return layer.msg("最多上传1张图片")
    179. }
    180. },
    181. })

    结束

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

  • 相关阅读:
    Golang 结构化日志包 log/slog 详解(六):slog.Record
    OpenTiny Vue 3.11.0 发布:增加富文本、ColorPicker等4个新组件,迎来了贡献者大爆发!
    【附代码案例】深入理解 PyTorch 张量:叶子张量与非叶子张量
    SpringBoot整合web放回json数据的方法
    基于注意力机制的LSTM液体管道非稳态工况检测
    【云原生 | 从零开始学Kubernetes】十、k8sPod节点亲和性和反亲和性
    第一章、基于Ruoyi-Vue开发脚手架之对象存储实现
    异步FIFO设计
    使用VBS编写xshell/SecureCRT自动化脚本
    vue3的ref,reactive的使用和原理解析
  • 原文地址:https://blog.csdn.net/m0_75116475/article/details/132619646