epcSimpleDetailOne.vue 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. <template>
  2. <view class="box">
  3. <view class="zdyNavBox">
  4. <view class="status_bar" :style="{height: iStatusBarHeight + 'px'}"></view>
  5. <view class="zdyNav">
  6. <view class="zdyNavLeft">
  7. <div @click="goback" class="uni-page-head-btn"><i class="uni-btn-icon"
  8. style="color: rgb(0, 0, 0); font-size: 27px;"></i></div>
  9. </view>
  10. <view class="xx" style="position: absolute;left:100rpx;color: black;font-size: 23px;" @click="goToSelectCarModel">X</view>
  11. <view class="zdyNavTitle" @click="changeIsShow()">
  12. <view class="title">{{chlildObj.title}}</view>
  13. <view class="sanjiao" v-if="isShowAll == 1">▲</view>
  14. <view class="sanjiao" v-if="isShowAll == 0">▼</view>
  15. </view>
  16. <view style="width: 120rpx;"></view>
  17. </view>
  18. <view class="title-box" v-if="isShowAll == 1" style="z-index: 999;">
  19. <view class="item" v-for="(item,index) in allChildrenObj" :key="index" @click="clickTitle(item)">
  20. <view class="item-image">
  21. <image :src="item.image" mode="aspectFit" style="width: 100%;height: 180rpx;"></image>
  22. </view>
  23. <view class="item-title" >
  24. {{item.title}}
  25. </view>
  26. </view>
  27. <view v-if="isShowAll == 1" style="height: 100vh;;z-index: 998;" @click="closeTitleTab()"></view>
  28. <view v-if="isShowAll == 1" style="height: 100vh;;z-index: 998;" @click="closeTitleTab()"></view>
  29. <view v-if="isShowAll == 1" style="height: 100vh;;z-index: 998;" @click="closeTitleTab()"></view>
  30. </view>
  31. </view>
  32. <view class="box-image-big" v-if="showType == 0">
  33. <view class="image-big">
  34. <image :src="chlildObj.image" mode="aspectFit" style="width: 100%;height: 80vh"></image>
  35. <canvas canvas-id="big-this-image" class="drawing-canvas" id="big-this-image" type="2d"
  36. @touchstart="handleTouchStartBig"
  37. @touchmove="handleTouchMoveBig"
  38. @touchend="handleTouchEndBig"></canvas>
  39. </view>
  40. <view class="box-btn">
  41. <view class="leftBtn" @click="getOther(-1)"><</view>
  42. <view class="rightBtn"@click="getOther(1)">></view>
  43. </view>
  44. </view>
  45. <view class="box-image-small" v-if="showType == 1" @tap="handleImageClick">
  46. <view class="small-image" >
  47. <image class="small-this-image" :src="chlildObj.image" mode="aspectFill" ></image>
  48. <!-- Canvas遮罩层 -->
  49. <canvas
  50. canvas-id="mask-canvas"
  51. class="mask-canvas"
  52. :style="{
  53. position: 'absolute',
  54. top: 0,
  55. left: 0,
  56. width: '100%',
  57. height: '100%',
  58. zIndex: 1,
  59. pointerEvents: 'none'
  60. }"
  61. ></canvas>
  62. </view>
  63. </view>
  64. <view class="box-jiantou">
  65. <view class="jian-span " :class="{'jian-down':isRotated}" @click="changeIsRotated()">》</view>
  66. </view>
  67. <view class="box-lab">
  68. <scroll-view scroll-x=true enable-flex=true show-scrollbar=false class="box-lab-scr" style="white-space: nowrap;height: 90rpx;" :scroll-into-view="scrollIntoId">
  69. <view class="lab-titles" :class="{'clickLab':labtitile==index}" :id="'item-' + index" @click="labTitile(item,index)" v-for="(item,index) in chlildObj.children" :key="index">
  70. {{item.title}}
  71. </view>
  72. </scroll-view>
  73. </view>
  74. <view class="lab-grid" v-if="showType == 1">
  75. <view class="lab-child" v-for="(item,index) in labChildObj" :key="index" @click="labChildClick(item)">
  76. <view class="lab-child-image">
  77. <image :src="item.image" mode="aspectFit" style="width: 160rpx;height: 150rpx;"></image>
  78. </view>
  79. <view class="lab-child-title">
  80. {{item.title}}
  81. </view>
  82. </view>
  83. </view>
  84. </view>
  85. </template>
  86. <script>
  87. export default {
  88. data() {
  89. return {
  90. toke:"",
  91. param:"",
  92. access_time:"",
  93. epc_id:"",
  94. brand_id:"",
  95. iStatusBarHeight: '',
  96. chlildObj:null,
  97. allChildrenObj:null,
  98. isShowAll:0,
  99. isRotated: false,
  100. labtitile: 0,
  101. labChildObj:null,
  102. showType:0, //0大图1小图
  103. ctxBig: null, //大画布
  104. pointsBig: [], // 存储所有点的坐标数组
  105. isDrawingBig: false, // 是否正在绘制的标志
  106. currentPathBig: [], // 当前路径的点数组
  107. ctxMask: null, //小画布
  108. pointsSmall: [], // 存储所有点的坐标数组
  109. isDrawingSmall: false, // 是否正在绘制的标志
  110. currentPathSmall: [], // 当前路径的点数组
  111. hitChild:[],//圈选中对象
  112. thisWidth: 0,//当前屏幕宽度
  113. scrollIntoId: '', //工东严肃定位
  114. specifiedAreas: [], //预定义位置
  115. unmaskAreas: [], // 已取消遮罩的区域
  116. unmaskSize: 80,
  117. brand_name:'',
  118. caption:'',
  119. }
  120. },
  121. onLoad(opt) {
  122. this.iStatusBarHeight = uni.getSystemInfoSync().statusBarHeight;
  123. //当前对象
  124. this.chlildObj = uni.getStorageSync('epcChildren');
  125. //全部对象
  126. this.allChildrenObj = uni.getStorageSync('epcAllChildren');
  127. //当前详细
  128. this.labChildObj = this.chlildObj.children[this.labtitile].children;
  129. this.token = opt.token;
  130. this.param = opt.param;
  131. this.access_time = opt.access_time;
  132. this.epc_id = opt.epc_id;
  133. this.brand_id = opt.brand_id;
  134. this.brand_name = opt.brand_name;
  135. this.caption = opt.caption;
  136. this.getSmileDate();
  137. },
  138. onReady(){
  139. this.initCanvas();
  140. },
  141. onShow() {
  142. this.initCanvas(); // 确保重新加载数据
  143. },
  144. methods: {
  145. goback() {
  146. uni.navigateBack({})
  147. },
  148. //返回选品牌
  149. goToSelectCarModel(){
  150. uni.navigateTo({url:'SelectCarModel'});
  151. },
  152. //标题title点击事件
  153. changeIsShow(){
  154. this.isShowAll = this.isShowAll == 0 ? 1 : 0;
  155. },
  156. //上下箭头点击事件
  157. changeIsRotated(){
  158. this.isRotated = !this.isRotated;
  159. this.showType = this.isRotated ? 1:0;
  160. if (this.showType == 1) {
  161. this.$nextTick(() => {
  162. this.initSpecifiedAreasPush();
  163. });
  164. }else{
  165. this.clearUnmaskAreas()
  166. }
  167. },
  168. //左右按钮点击
  169. getOther(typeNum){
  170. //获取顺序
  171. let index = 0;
  172. for (var i = 0; i < this.allChildrenObj.length; i++) {
  173. let item = this.allChildrenObj[i]
  174. if (item.id == this.chlildObj.id){
  175. index = i;
  176. }
  177. }
  178. if(typeNum ==-1){
  179. index = index-1 < 0 ? this.allChildrenObj.length-1 : index-1 ;
  180. }else{
  181. index = index+1 > this.allChildrenObj.length-1 ? 0 : index+1 ;
  182. }
  183. this.chlildObj = this.allChildrenObj[index];
  184. this.labChildObj = this.chlildObj.children[index].children;
  185. },
  186. //标题下拉图片点击
  187. clickTitle(item){
  188. this.chlildObj = item;
  189. this.isShowAll = 0;
  190. this.showType = 0;
  191. this.initCanvas();
  192. this.getSmileDate();
  193. this.initSpecifiedAreasPush();
  194. },
  195. //labtitiel的点击事件
  196. labTitile(item,index){
  197. this.showType = 1;
  198. this.isRotated = true;
  199. this.labtitile = index;
  200. this.labChildObj = this.chlildObj.children[this.labtitile].children;
  201. this.scrollIntoId = 'item-' + index;
  202. //set 小图坐标
  203. //this.specifiedAreasPush(item);
  204. //延时等待渲染效果
  205. setTimeout(()=>{
  206. this.initSpecifiedAreasPush();
  207. },100)
  208. },
  209. //下布局grid点击图片事件
  210. labChildClick(item){
  211. uni.navigateTo({
  212. url: 'SimpleOemSearch?brand=' + undefined + '&token=' + this.token + '&param=' +
  213. this.param + '&access_time=' + this.access_time + '&title=' + item.title+'&epc_id='+this.epc_id+"&brand_name="+this.brand_name
  214. +"&caption="+this.caption
  215. })
  216. },
  217. //关闭title 下拉
  218. closeTitleTab(){
  219. this.isShowAll = 0;
  220. },
  221. //初始化画布
  222. initCanvas(){
  223. this.ctxBig = uni.createCanvasContext('big-this-image',this);
  224. //this.ctxSmall = uni.createCanvasContext('small-this-image',this);
  225. this.ctxMask = uni.createCanvasContext('mask-canvas', this);
  226. // 设置线条颜色
  227. this.ctxBig.setStrokeStyle("red");
  228. // 设置线宽
  229. this.ctxBig.setLineWidth(5);
  230. // 设置线条末端为圆形
  231. this.ctxBig.setLineCap("round");
  232. // 设置线条连接处为圆形
  233. this.ctxBig.setLineJoin("round");
  234. this.ctxBig.setLineDash([]); // 确保不是虚线
  235. // 设置更高的绘制质量
  236. this.ctxBig.setGlobalAlpha(1);
  237. this.ctxBig.setShadow(0, 0, 0, 'transparent'); // 清除阴影避免性能问题
  238. this.drawFullMask();
  239. },
  240. //小图遮罩初始化显示 正前
  241. initSpecifiedAreasPush(){
  242. let id = 0;
  243. //获取这是第几个labtitle
  244. const element = document.querySelector('.clickLab');
  245. if (element) {
  246. id = element.id.split("-")[1];
  247. }
  248. let tetxx = this.chlildObj.children[id].title;
  249. let isAreas = {"title":tetxx};
  250. this.specifiedAreasPush(isAreas);
  251. },
  252. // 处理触摸开始事件
  253. handleTouchStartBig(e) {
  254. // 获取触摸点的x,y坐标
  255. const x = e.touches[0].x;
  256. const y = e.touches[0].y;
  257. // 设置正在绘制标志
  258. this.isDrawingBig = true;
  259. // 初始化当前路径数组,并添加第一个点
  260. this.currentPathBig = [{x, y}];
  261. // 开始新路径
  262. this.ctxBig.beginPath();
  263. // 移动画笔到起始点
  264. this.ctxBig.moveTo(x, y);
  265. },
  266. // 处理触摸移动事件
  267. handleTouchMoveBig(e) {
  268. // 如果不是绘制状态则返回
  269. if (!this.isDrawingBig) return;
  270. // 获取当前触摸点的坐标
  271. const x = e.touches[0].x;
  272. const y = e.touches[0].y;
  273. // 将当前点添加到当前路径数组
  274. this.currentPathBig.push({x, y});
  275. // 当有足够点数时使用贝塞尔曲线
  276. if (this.currentPathBig.length >= 3) {
  277. this.drawSmoothLine();
  278. } else {
  279. this.drawStraightLine();
  280. }
  281. },
  282. // 绘制平滑曲线
  283. drawSmoothLine() {
  284. const points = this.currentPathBig;
  285. const len = points.length;
  286. if (len < 3) return;
  287. this.ctxBig.beginPath();
  288. this.ctxBig.moveTo(points[0].x, points[0].y);
  289. // 使用二次贝塞尔曲线平滑连接
  290. for (let i = 1; i < len - 2; i++) {
  291. const controlX = (points[i].x + points[i + 1].x) / 2;
  292. const controlY = (points[i].y + points[i + 1].y) / 2;
  293. this.ctxBig.quadraticCurveTo(points[i].x, points[i].y, controlX, controlY);
  294. }
  295. // 处理最后两个点
  296. if (len >= 3) {
  297. this.ctxBig.quadraticCurveTo(
  298. points[len - 2].x,
  299. points[len - 2].y,
  300. points[len - 1].x,
  301. points[len - 1].y
  302. );
  303. }
  304. this.ctxBig.stroke();
  305. this.ctxBig.draw(true);
  306. },
  307. // 绘制直线(点数不足时)
  308. drawStraightLine() {
  309. if (this.currentPathBig.length < 2) return;
  310. this.ctxBig.beginPath();
  311. this.ctxBig.moveTo(this.currentPathBig[0].x, this.currentPathBig[0].y);
  312. this.ctxBig.lineTo(this.currentPathBig[1].x, this.currentPathBig[1].y);
  313. this.ctxBig.stroke();
  314. this.ctxBig.draw(true);
  315. },
  316. // 处理触摸结束事件
  317. handleTouchEndBig() {
  318. // 如果不是绘制状态则返回
  319. if (!this.isDrawingBig) return;
  320. // 将当前路径的所有点合并到总点数组中
  321. this.pointsBig = [...this.pointsBig, ...this.currentPathBig];
  322. // 清空当前路径
  323. //this.currentPathBig = [];
  324. // 重置绘制状态
  325. this.isDrawingBig = false;
  326. //清空圈选对象
  327. this.hitChild = [];
  328. setTimeout(()=>{
  329. const width = this.ctxBig.width || 1024; // 如果没有设置width属性,使用默认值
  330. const height = this.ctxBig.height || 1024;
  331. this.ctxBig.clearRect(0,0,width,height);
  332. this.ctxBig.draw(true);
  333. this.isDrawingBig = true;
  334. this.toDetailByCanvas();
  335. },1000)
  336. },
  337. //筛选圈选的位置
  338. toDetailByCanvas(){
  339. //坐标集合
  340. let zuobiao = this.currentPathBig,
  341. //当前对象
  342. chlildObj = this.labChildObj;
  343. //击中对象
  344. const oneSet = new Set();
  345. zuobiao.forEach(x =>{
  346. //判断当前坐标是否在当前位置
  347. this.ifClickObj(x.x,x.y);
  348. //跳转
  349. uni.setStorageSync('epcChildrenTwo', this.hitChild);
  350. uni.navigateTo({
  351. url: 'epcSimpleDetailTwo?token='+this.token+'&param='+this.param+'&access_time='+this.access_time+'&epc_id='+this.epc_id+"&title="+this.chlildObj.title
  352. +'&brand_id='+this.brand_id+"&brand_name="+this.brand_name+"&caption="+this.caption
  353. })
  354. })
  355. },
  356. /**
  357. * @param {Object} x
  358. * @param {Object} y
  359. * 要遍历多次,第一次为外面的大类确定
  360. * 第二次为大类里面的包含的小件
  361. */
  362. //根据坐标判断是否击中集合
  363. ifClickObj(x,y){
  364. let isDelete = 0;
  365. let chlildObj = this.chlildObj; //顶级对象
  366. this.thisWidth = uni.getSystemInfoSync().screenWidth;
  367. chlildObj.children.forEach(item => {
  368. let item_width = item.width,
  369. item_top = item.top,
  370. item_left = item.left,
  371. item_height = item.height,
  372. image_width = chlildObj.width,
  373. image_height = chlildObj.height;
  374. //计算实际坐标
  375. let x1 = 0,//x头
  376. x2 = 0,//x尾
  377. y1 = 0,//y头
  378. y2 = 0;//y尾
  379. x1 = this.pxToX(item_left,image_width);
  380. x2 = x1+this.pxToX(item_width,image_width);
  381. y1 =this.pxToY(item_top,image_width,image_height);
  382. y2 = y1+this.pxToY(item_height,image_width,image_height);
  383. //命中父级组件
  384. if(x>x1 && x<x2 && y>y1 && y<y2){
  385. let childrenParent = this.hitChild.find(x1 => x1.title === item.title);
  386. if(childrenParent==undefined || childrenParent.length == "" ){
  387. childrenParent = {
  388. "id":item.id,
  389. "children":[],
  390. "image":item.image,
  391. "pid":item.pid,
  392. "title":item.title,
  393. "is_visible":item.is_visible,
  394. "height":item.height,
  395. "top":item.top,
  396. "left":item.left,
  397. "width":item.width
  398. };
  399. this.hitChild.push(childrenParent);
  400. }
  401. //判断命中小组建
  402. item.children.forEach(childItem =>{
  403. let child_width = childItem.width,
  404. child_top = childItem.top,
  405. child_left = childItem.left,
  406. child_height = childItem.height,
  407. child_x = 0,
  408. child_x2 = 0,
  409. child_y = 0,
  410. child_y2 = 0;
  411. child_x = this.pxToX(child_width,image_width);
  412. child_x2 = child_x+this.pxToX(child_width,image_width);
  413. child_y = this.pxToY(child_top,image_width,image_height);
  414. child_y2 = child_y+this.pxToY(child_height,image_width,image_height);
  415. //命中小组件
  416. if(x>child_x && x<child_x2 && y>child_y && y<child_y2){
  417. //判断是否存在
  418. let hitchilditem = childrenParent.children.find(mm => mm.title === childItem.title);
  419. if(hitchilditem == undefined || hitchilditem == ""){
  420. childrenParent.children.push(childItem);
  421. }
  422. }
  423. })
  424. }
  425. })
  426. },
  427. /**
  428. * 像素等比例转换X轴
  429. * @param {Object} px 标记像素
  430. * @param {Object} widch 图片宽度
  431. */
  432. pxToX(px,width){
  433. let pxx = Math.floor((px / width) * this.thisWidth);
  434. return pxx;
  435. },
  436. /**
  437. * 像素等比例转换Y轴
  438. * @param {Object} px 标记像素
  439. * @param {Object} height 图片高度
  440. */
  441. pxToY(px,width,height){
  442. //计算等比例高度
  443. const scaledHeight = (this.thisWidth / width) * height;
  444. let pxx = Math.floor(( px/ height) * scaledHeight);
  445. return pxx;
  446. },
  447. // 清除所有取消遮罩的区域
  448. getSmileDate(){
  449. this.chlildObj.children.forEach(item => {
  450. let chlildObj = this.chlildObj;
  451. let item_width = item.width,
  452. item_top = item.top,
  453. item_left = item.left,
  454. item_height = item.height,
  455. image_width = chlildObj.width,
  456. image_height = chlildObj.height;
  457. let x1 = 0,
  458. x2 = 0,
  459. y1 = 0,
  460. y2 = 0;
  461. let oldx = item_left,
  462. oldy = item_top,
  463. oldx2 = item_width*1,
  464. oldy2 = item_height*1;
  465. x1 = oldy*1;
  466. x2 = oldy*1 + oldy2*1;
  467. y1 = image_width*1 - oldx*1;
  468. y2 = image_width*1 - oldx*1 - oldx2*1;
  469. // y1 = item_left;
  470. // x1 = item_top;
  471. // y2 = item_left*1 + item_width*1;
  472. // x2 = item_top*1 + item_height*1;
  473. let zb1 = this.shuToHeng(image_width,image_height,x1,y1);
  474. let zb2 = this.shuToHeng(image_width,image_height,x2,y2);
  475. this.specifiedAreas.push({
  476. "x1":zb1.x,"y1":zb1.y,"x2":zb2.x,"y2":zb2.y,id:'area'+item.title
  477. })
  478. })
  479. },
  480. //push 取消遮罩
  481. specifiedAreasPush(item){
  482. let allAreas = this.specifiedAreas;
  483. //获取tab坐标
  484. let isAreas = allAreas.find(x => x.id == "area"+item.title);
  485. this.unmaskAreas = [];
  486. this.unmaskAreas.push({
  487. id: isAreas.id,
  488. x1: isAreas.x1,
  489. y1: isAreas.y1,
  490. x2: isAreas.x2,
  491. y2: isAreas.y2,
  492. radius: this.unmaskSize/2
  493. });
  494. // 重绘Canvas遮罩
  495. this.updateMaskCanvas();
  496. },
  497. //计算横置小图的坐标,竖坐标转化成横坐标,宽高也改变了
  498. /**
  499. * @param {Object} width 图片宽度
  500. * @param {Object} height 图片高度
  501. * @param {Object} x 坐标
  502. * @param {Object} y 坐标
  503. * @param {Object} thisWidth 当前屏幕宽度
  504. */
  505. shuToHeng(width,height,x,y){
  506. //转换后的宽度
  507. let thisWidth = uni.getSystemInfoSync().screenWidth;
  508. //选装的高度
  509. let thisHeight = (thisWidth / height) * width;
  510. //旋转后的y轴
  511. let pxy = Math.floor(( y/ width) * thisHeight);
  512. //旋转后的X轴
  513. let pxx = Math.floor((x / height) * thisWidth);
  514. return {"x":pxx,"y":pxy};
  515. },
  516. //start
  517. handleImageClick(event) {
  518. const query = uni.createSelectorQuery().in(this);
  519. query.select('.small-image').boundingClientRect(data => {
  520. if (data) {
  521. const clickX = event.touches[0].clientX - data.left;
  522. const clickY = event.touches[0].clientY - data.top;
  523. // 检查是否点击在指定区域内
  524. const hitArea = this.checkClickInSpecifiedArea(clickX, clickY);
  525. if (hitArea) {
  526. // 如果点击在指定区域内,取消该区域遮罩
  527. this.addUnmaskArea(hitArea, clickX, clickY);
  528. }
  529. }
  530. }).exec();
  531. },
  532. // 检查点击是否在指定区域内
  533. checkClickInSpecifiedArea(clickX, clickY) {
  534. for (let area of this.specifiedAreas) {
  535. // 检查点击坐标是否在区域内
  536. if (clickX >= area.x1 &&
  537. clickX <= area.x2 &&
  538. clickY >= area.y2 &&
  539. clickY <= area.y1) {
  540. return area;
  541. }
  542. }
  543. return null;
  544. },
  545. // 添加取消遮罩区域
  546. addUnmaskArea(area, x, y) {
  547. const exists = this.unmaskAreas.some(item => item.id === area.id);
  548. if (!exists) {
  549. this.unmaskAreas = [];
  550. this.unmaskAreas.push({
  551. id: area.id,
  552. x1: area.x1,
  553. y1: area.y1,
  554. x2: area.x2,
  555. y2: area.y2,
  556. radius: this.unmaskSize/2
  557. });
  558. //lab标题获取焦点
  559. let childrenFor = this.chlildObj.children;
  560. let index = 0;
  561. for (var i = 0; i < childrenFor.length; i++) {
  562. let item = childrenFor[i];
  563. if(item.title == area.id.split("area")[1]){
  564. index = i;
  565. this.labTitile(item,i);
  566. }
  567. }
  568. }
  569. },
  570. // 更新Canvas遮罩
  571. updateMaskCanvas() {
  572. const ctx = this.ctxMask;
  573. // 1. 清除画布(动态获取尺寸)
  574. const { windowWidth, windowHeight } = uni.getSystemInfoSync();
  575. ctx.clearRect(0, 0, windowWidth, windowHeight);
  576. // 2. 绘制全屏半透明遮罩
  577. ctx.setFillStyle('rgba(255, 255, 255, 0.5)');
  578. ctx.fillRect(0, 0, windowWidth, windowHeight);
  579. // 3. 关键!设置合成模式为“擦除”
  580. ctx.globalCompositeOperation = 'destination-out';
  581. ctx.setFillStyle('rgba(0, 0, 0, 1)'); // 颜色不重要,alpha通道生效
  582. // 4. 绘制取消遮罩的区域(矩形示例)
  583. this.unmaskAreas.forEach(area => {
  584. ctx.beginPath();
  585. ctx.moveTo(area.x1, area.y1);
  586. ctx.lineTo(area.x2, area.y1);
  587. ctx.lineTo(area.x2, area.y2);
  588. ctx.lineTo(area.x1, area.y2);
  589. ctx.closePath();
  590. ctx.fill();
  591. });
  592. // 5. 恢复默认合成模式
  593. ctx.globalCompositeOperation = 'source-over';
  594. // 6. 提交绘制(微信小程序需调用 draw)
  595. ctx.draw(true); // 注意:微信小程序中需传 true 表示异步绘制
  596. },
  597. // 清除所有取消遮罩的区域
  598. clearUnmaskAreas() {
  599. this.unmaskAreas = [];
  600. const ctx = this.ctxMask;
  601. const { windowWidth, windowHeight } = uni.getSystemInfoSync();
  602. // 1. 清除画布(使用实际尺寸)
  603. ctx.clearRect(0, 0, windowWidth * 2, windowHeight * 2); // 考虑retina屏幕
  604. ctx.draw(false, () => {
  605. uni.hideLoading(); // 如果之前有loading可以隐藏
  606. });
  607. },
  608. // 绘制全屏遮罩
  609. drawFullMask() {
  610. const ctx = this.ctxMask;
  611. // 获取实际屏幕尺寸(更精确的方式)
  612. const { windowWidth, windowHeight } = uni.getSystemInfoSync();
  613. // 1. 清除画布(使用实际尺寸)
  614. ctx.clearRect(0, 0, windowWidth * 2, windowHeight * 2); // 考虑retina屏幕
  615. // 2. 绘制全屏遮罩
  616. ctx.setFillStyle('rgba(255, 255, 255, 0.5)');
  617. ctx.fillRect(0, 0, windowWidth * 2, windowHeight * 2);
  618. ctx.draw(false, () => {
  619. uni.hideLoading(); // 如果之前有loading可以隐藏
  620. });
  621. },
  622. }
  623. }
  624. </script>
  625. <style scoped>
  626. .zdyNavBox {
  627. width: 100vw;
  628. background: #FFFFFF;
  629. position: fixed;
  630. top: 0;
  631. left: 0;
  632. z-index: 9999999;
  633. }
  634. .zdyNav {
  635. display: flex;
  636. justify-content: space-between;
  637. align-items: center;
  638. padding: 14rpx 6rpx;
  639. }
  640. .zdyNavLeft {
  641. width: 120rpx;
  642. }
  643. .zdyNavTitle {
  644. display: flex;
  645. }
  646. .zdyNavRight {
  647. background: #FFFFFF;
  648. text-align: center;
  649. font-size: 28rpx;
  650. color: #3F90F7;
  651. width: 120rpx;
  652. }
  653. .box{
  654. background: #999999;
  655. }
  656. .title{
  657. background: #FFFFFF;
  658. text-align: center;
  659. font-size: 32rpx;
  660. color: #488CC3;
  661. font-size: 25rpx;
  662. }
  663. .sanjiao{
  664. font-size: 18rpx;
  665. color: #488CC3;
  666. padding: 5rpx;
  667. /* right: 42%;
  668. top: 40%; */
  669. }
  670. .title-box{
  671. display: grid;
  672. grid-template-columns: repeat(3,1fr);
  673. gap: 5rpx;
  674. padding-bottom: 20rpx;
  675. .item{
  676. margin: 30rpx 10rpx;
  677. height: 220rpx;
  678. .item-image{
  679. border: solid 1rpx #999999;
  680. border-radius: 10rpx;
  681. padding-bottom: 10rpx;
  682. }
  683. .item-title{
  684. text-align: center;
  685. }
  686. }
  687. }
  688. .box-image-big{
  689. background: #ffffff;
  690. height: 80vh;
  691. .image-big{
  692. position: relative;
  693. width: 100%;
  694. height: 80vh;
  695. }
  696. .drawing-canvas {
  697. position: absolute;
  698. top: 0;
  699. left: 0;
  700. width: 100%;
  701. height: 100%;
  702. z-index: 10;
  703. }
  704. .box-btn{
  705. display: flex;
  706. justify-content: space-between;
  707. position: relative;
  708. top: -50%;
  709. z-index: 99999;
  710. .leftBtn{
  711. width: 40rpx;
  712. height: 100rpx;
  713. background: #999999;
  714. color: #ffffff;
  715. font-size: 35rpx;
  716. line-height: 100rpx;
  717. text-align: center;
  718. border-radius: 0 15rpx 15rpx 0;
  719. }
  720. .rightBtn{
  721. width: 40rpx;
  722. height: 100rpx;
  723. background: #999999;
  724. color: #ffffff;
  725. font-size: 35rpx;
  726. line-height: 100rpx;
  727. text-align: center;
  728. border-radius: 15rpx 0 0 15rpx;
  729. }
  730. }
  731. }
  732. .box-image-small{
  733. z-index: 999;
  734. .small-image{
  735. width: 100%;
  736. height: 420rpx;
  737. background: #ffffff;
  738. position: relative; /* 关键:为遮罩层提供定位基准 */
  739. .small-this-image{
  740. transform: rotate(-90deg);
  741. height: 750rpx;
  742. width: 420rpx;
  743. margin: -150rpx 165rpx;
  744. }
  745. }
  746. }
  747. .image-mask {
  748. position: absolute;
  749. top: 0;
  750. left: 0;
  751. width: 100%;
  752. height: 100%;
  753. background: rgba(255, 255, 255, 0.5);
  754. pointer-events: none;
  755. z-index: 1; /* 确保在取消区域之下 */
  756. }
  757. /* 双箭头核心实现 */
  758. .box-jiantou {
  759. background: #ffffff;
  760. text-align: center;
  761. .jian-span{
  762. width: 20rpx;
  763. transform: rotate(-90deg);
  764. transform-origin: center;
  765. margin: 0 auto;
  766. }
  767. .jian-down{
  768. transform: rotate(90deg);
  769. transform-origin: center;
  770. }
  771. }
  772. .box-lab{
  773. background: #ffffff;
  774. z-index: 999;
  775. }
  776. .box-lab-scr{
  777. .uni-scroll-view-content{
  778. display: flex;
  779. flex-direction: row;
  780. flex-wrap: nowrap;
  781. white-space: nowrap;
  782. }
  783. }
  784. .lab-titles{
  785. width: 130rpx !important;
  786. height: 60rpx;
  787. font-size: 35rpx;
  788. color: #999999;
  789. padding-left: 5rpx;
  790. margin: 10rpx;
  791. padding-right: 8rpx;
  792. text-align: center;
  793. }
  794. .clickLab{
  795. color: #236DB1;
  796. border-bottom: 4rpx solid #236DB1;
  797. }
  798. .lab-grid{
  799. display: grid;
  800. grid-template-columns: repeat(4,1fr);
  801. background: #ffffff;
  802. gap: 2rpx;
  803. .lab-child{
  804. padding: 10rpx;
  805. .lab-child-image{
  806. border: 1rpx solid #999999;
  807. border-radius: 8rpx;
  808. }
  809. .lab-child-title{
  810. text-align: center;
  811. font-size: 24rpx;
  812. }
  813. }
  814. }
  815. </style>