<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Uniapp 實現(xiàn)新手引導(dǎo)訪問功能組件

      最近有個需求需要在小程序中實現(xiàn)一個新手引導(dǎo)組件,通過遮罩、高亮區(qū)域和提示框的組合,為應(yīng)用提供流暢的用戶引導(dǎo)體驗。

      組件功能概述

      這個引導(dǎo)組件提供了以下核心功能:

      • 分步引導(dǎo):支持多步驟引導(dǎo)流程
      • 智能定位:自動計算高亮區(qū)域位置
      • 遮罩效果:突出顯示目標(biāo)元素
      • 方向感知:根據(jù)位置調(diào)整提示框方向
      • 進(jìn)度控制:下一步/跳過/完成操作
      • 狀態(tài)保存:使用 localStorage 記錄完成狀態(tài)(已取消,可擴展)

      核心實現(xiàn)代碼分析

      組件模板結(jié)構(gòu)

      <template>
        <view v-if="visible" class="guide-mask">
          <!-- 遮罩四塊 -->
          <view class="mask-piece top" :style="maskStyles.top"></view>
          <view class="mask-piece bottom" :style="maskStyles.bottom"></view>
          <view class="mask-piece left" :style="maskStyles.left"></view>
          <view class="mask-piece right" :style="maskStyles.right"></view>
      
          <!-- 高亮區(qū)域 -->
          <view
            v-if="currentStep"
            class="highlight"
            :style="highlightStyleStr"
          ></view>
      
          <!-- 提示框 -->
          <view class="tooltip" :style="tooltipStyleStr">
            <text class="tip-text">{{ currentStep.tip }}</text>
            <view class="tip-arrow" :class="currentStep.tipPosition || 'left'"></view>
            <view class="btns">
              <button @tap="nextStep">{{ isLast ? "完成" : "下一步" }}</button>
              <button class="skip" @tap="skip">跳過</button>
            </view>
          </view>
      
          <!-- 引導(dǎo)機器人圖標(biāo):這個也可以是別的圖標(biāo),這邊用的是機器人圖標(biāo) -->
          <image
            class="robot-img"
            src="更換為自己的圖標(biāo)"
            :style="tooltipStyleImg"
            mode="widthFix"
          />
        </view>
      </template>
      

      組件邏輯實現(xiàn)

      export default {
        props: {
          steps: { type: Array, required: true }, // 引導(dǎo)步驟配置
          guideKey: { type: String, default: "default_guide_key" }, // 引導(dǎo)標(biāo)識鍵,用于確認(rèn)是否做過引導(dǎo),可以擴展
        },
        data() {
          return {
            stepIndex: 0, // 當(dāng)前步驟索引
            visible: false, // 是否顯示引導(dǎo)
          };
        },
        computed: {
          // 當(dāng)前步驟配置
          currentStep() {
            return this.steps[this.stepIndex];
          },
      
          // 是否為最后一步
          isLast() {
            return this.stepIndex === this.steps.length - 1;
          },
      
          // 高亮區(qū)域樣式
          highlightStyleStr() {
            // 計算樣式邏輯...
          },
      
          // 機器人圖標(biāo)位置
          tooltipStyleImg() {
            // 根據(jù)提示位置計算坐標(biāo)...
          },
      
          // 提示框樣式
          tooltipStyleStr() {
            // 根據(jù)位置計算提示框方向...
          },
      
          // 遮罩層樣式計算
          maskStyles() {
            // 計算四塊遮罩的位置和尺寸...
          },
        },
        methods: {
          // 開始引導(dǎo)
          start(force = false) {
            if (!force) return;
            this.stepIndex = 0;
            this.visible = true;
          },
      
          // 下一步
          nextStep() {
            this.isLast ? this.finish() : this.stepIndex++;
          },
      
          // 跳過引導(dǎo)
          skip() {
            this.finish();
          },
      
          // 完成引導(dǎo)
          finish() {
            this.visible = false;
            this.$emit("finish");
            localStorage.setItem(this.guideKey, "completed");
          },
        },
      };
      

      樣式實現(xiàn)

      .guide-mask {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        z-index: 1000001;
      }
      
      .highlight {
        position: absolute;
        border: 2px solid #fff;
        border-radius: 8px;
        box-shadow: 0 0 10px #fff;
      }
      
      .tooltip {
        position: absolute;
        background: white;
        padding: 10px 16px;
        border-radius: 12px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
        min-width: 250rpx;
      }
      
      /* 箭頭方向樣式 */
      .tip-arrow.bottom {
        bottom: -8px;
        border-left: 8px solid transparent;
        border-right: 8px solid transparent;
        border-top: 8px solid white;
      }
      
      /* 其他方向樣式... */
      
      .robot-img {
        position: absolute;
        width: 150rpx;
        z-index: 10003;
      }
      

      關(guān)鍵實現(xiàn)技術(shù)

      1. 智能遮罩計算

      組件將遮罩分為四個部分(上、下、左、右),通過計算目標(biāo)元素的位置動態(tài)設(shè)置每塊遮罩的尺寸:

      maskStyles() {
      	const { top, left, width, height } = this.currentStep
      	const windowWidth = uni.getSystemInfoSync().windowWidth
      	const windowHeight = uni.getSystemInfoSync().windowHeight
      
      	return {
      		top: `... height: ${top}px; ...`,
      		bottom: `... top: ${top + height}px; height: ${windowHeight - (top + height)}px; ...`,
      		left: `... top: ${top}px; width: ${left}px; height: ${height}px; ...`,
      		right: `... left: ${left + width}px; width: ${windowWidth - (left + width)}px; ...`
      	}
      }
      

      2. 動態(tài)提示框定位

      根據(jù)目標(biāo)元素位置自動調(diào)整提示框方向:

      tooltipStyleStr() {
      	const top = this.currentStep.top + this.currentStep.height + 10
      	const left = this.currentStep.left
      	const right = this.currentStep.right
      	const tipPosition = this.currentStep.tipPosition || 'left'
      	const { windowWidth } = uni.getSystemInfoSync();
      
      	return tipPosition === 'left'
      		? `right:${windowWidth - right}px;`
      		: `left:${left}px;`
      }
      

      3. 引導(dǎo)機器人位置計算

      根據(jù)提示方向計算機器人圖標(biāo)位置:

      tooltipStyleImg() {
      	const { top, left, width, height, tipPosition = 'left' } = this.currentStep
      	let x = 0, y = 0
      
      	switch (tipPosition) {
      		case 'left':
      			x = left - width
      			y = top + height
      			break
      		case 'right':
      			x = left + width / 5 * 3
      			y = top + height
      			break
      		// 其他情況...
      	}
      
      	return `top:${y}px;left:${x}px;`
      }
      

      完整代碼

      <template>
        <view v-if="visible" class="guide-mask">
          <!-- 遮罩四塊 -->
          <view class="mask-piece top" :style="maskStyles.top"></view>
          <view class="mask-piece bottom" :style="maskStyles.bottom"></view>
          <view class="mask-piece left" :style="maskStyles.left"></view>
          <view class="mask-piece right" :style="maskStyles.right"></view>
      
          <view
            v-if="currentStep"
            class="highlight"
            :style="highlightStyleStr"
          ></view>
      
          <view class="tooltip" :style="tooltipStyleStr">
            <text class="tip-text">{{ currentStep.tip }}</text>
            <view class="tip-arrow" :class="currentStep.tipPosition || 'left'"></view>
            <view class="btns">
              <button @tap="nextStep">{{ isLast ? "完成" : "下一步" }}</button>
              <button class="skip" @tap="skip">跳過</button>
            </view>
          </view>
      
          <image
            class="robot-img"
            src="@/images/static/robot.png"
            :style="tooltipStyleImg"
            mode="widthFix"
          />
        </view>
      </template>
      
      <script>
      export default {
        props: {
          steps: {
            type: Array,
            required: true,
          },
          guideKey: {
            type: String,
            default: "default_guide_key",
          },
        },
        data() {
          return {
            stepIndex: 0,
            visible: false,
          };
        },
        computed: {
          currentStep() {
            return this.steps[this.stepIndex];
          },
          isLast() {
            return this.stepIndex === this.steps.length - 1;
          },
          highlightStyleStr() {
            if (!this.currentStep) return "";
            const { top, left, width, height } = this.currentStep;
            return `position:absolute;top:${top}px;left:${left}px;width:${width}px;height:${height}px;border:2px solid #fff;box-shadow:0 0 10px #fff;border-radius:8px;z-index:10000;`;
          },
          tooltipStyleImg() {
            if (!this.currentStep) return "";
            const {
              top,
              left,
              width,
              height,
              tipPosition = "left",
            } = this.currentStep;
            let x = 0,
              y = 0;
            switch (tipPosition) {
              case "left":
                x = left - width;
                y = top + height;
                break;
              case "right":
                x = left + (width / 5) * 3;
                y = top + height;
                break;
              case "top":
                x = left + width / 2;
                y = top - 100; // 高度預(yù)估
                break;
              case "bottom":
              default:
                x = left + width / 2;
                y = top + height;
                break;
            }
      
            return `top:${y}px;left:${x}px;`;
          },
          tooltipStyleStr() {
            if (!this.currentStep) return "";
            const top = this.currentStep.top + this.currentStep.height + 10;
            const left = this.currentStep.left;
            const right = this.currentStep.right;
            const tipPosition = this.currentStep.tipPosition || "left";
            const { windowWidth } = uni.getSystemInfoSync();
            return (
              `position:absolute;top:${top}px;z-index:10001;` +
              (tipPosition === "left"
                ? `right:${windowWidth - right}px;`
                : `left:${left}px;`)
            );
          },
          maskStyles() {
            if (!this.currentStep) return {};
      
            const { top, left, width, height } = this.currentStep;
            const windowWidth = uni.getSystemInfoSync().windowWidth;
            const windowHeight = uni.getSystemInfoSync().windowHeight;
      
            return {
              top: `position: absolute; top: 0px; left: 0px; width: ${windowWidth}px; height: ${top}px; background: rgba(0, 0, 0, 0.6);`,
              bottom: `position: absolute; top: ${
                top + height
              }px; left: 0px; width: ${windowWidth}px; height: ${
                windowHeight - (top + height)
              }px; background: rgba(0, 0, 0, 0.6);`,
              left: `position: absolute; top: ${top}px; left: 0px; width: ${left}px; height: ${height}px; background: rgba(0, 0, 0, 0.6);`,
              right: `position: absolute; top: ${top}px; left: ${
                left + width
              }px; width: ${
                windowWidth - (left + width)
              }px; height: ${height}px; background: rgba(0, 0, 0, 0.6);`,
            };
          },
        },
        methods: {
          start(force = false) {
            if (!force) return;
            this.stepIndex = 0;
            this.visible = true;
          },
          nextStep() {
            if (this.isLast) {
              this.finish();
            } else {
              this.stepIndex++;
            }
          },
          skip() {
            this.finish();
          },
          finish() {
            this.visible = false;
            this.$emit("finish");
          },
        },
      };
      </script>
      
      <style scoped>
      .guide-mask {
        position: fixed;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        z-index: 1000001;
      }
      
      .mask-piece {
        position: absolute;
        background: rgba(0, 0, 0, 0.6);
      }
      
      .mask-layer {
        background: rgba(0, 0, 0, 0.6);
        width: 100%;
        height: 100%;
        position: absolute;
      }
      
      .highlight {
        position: absolute;
        border: 2px solid #fff;
        border-radius: 8px;
        box-shadow: 0 0 10px #fff;
      }
      
      .tooltip {
        /* box-shadow: 0 0 8px #0004; */
        position: absolute;
        background: white;
        padding: 10px 16px;
        border-radius: 12px;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
        font-size: 14px;
        color: #007aff;
        z-index: 10002;
        min-width: 250rpx;
      }
      
      .tip-arrow {
        position: absolute;
        width: 0;
        height: 0;
      }
      
      .tip-arrow.bottom {
        bottom: -8px;
        left: 20px;
        border-left: 8px solid transparent;
        border-right: 8px solid transparent;
        border-top: 8px solid white;
      }
      
      .tip-arrow.top {
        top: -8px;
        left: 20px;
        border-left: 8px solid transparent;
        border-right: 8px solid transparent;
        border-bottom: 8px solid white;
      }
      
      .tip-arrow.right {
        top: 12px;
        right: -8px;
        border-top: 8px solid transparent;
        border-bottom: 8px solid transparent;
        border-left: 8px solid white;
      }
      
      .tip-arrow.left {
        top: 12px;
        left: -8px;
        border-top: 8px solid transparent;
        border-bottom: 8px solid transparent;
        border-right: 8px solid white;
      }
      
      .robot-img {
        position: absolute;
        width: 150rpx;
        z-index: 10003;
      }
      
      .tip-text {
        font-size: 14px;
        color: #333;
      }
      
      .btns {
        display: flex;
        gap: 10px;
      }
      
      button {
        font-size: 12px;
        padding: 4px 8px;
      }
      
      .skip {
        color: #888;
      }
      </style>
      

      使用示例

      const guideSteps = [
      	{
      		tip: "這是 AI 聊天功能,點擊進(jìn)行聊天",
      		top: 100,
      		left: 50,
      		width: 200,
      		height: 40,
      		tipPosition: "bottom"
      	},
      	{
      		tip: "這是個人中心入口",
      		top: 500,
      		left: 300,
      		width: 80,
      		height: 80,
      		tipPosition: "left"
      	}
      ]
      
      // 在組件中使用
      <GuideMask :steps="guideSteps" guideKey="home_guide" @finish="onGuideFinish"/>
      

      組件不足

      1. tip&icon 定位:這里的組件定位主要是做了左右適配定位,如果需要兼容可以進(jìn)行擴展或者優(yōu)化
      2. 高亮區(qū)域:組件高亮區(qū)域當(dāng)前只是對于定位區(qū)域?qū)捀哌M(jìn)行高亮,可以做往外擴展,例如橢圓形的
      3. 跨頁面:目前只能對同個單一的頁面進(jìn)行引導(dǎo)式訪問,無法做到跨頁面跳轉(zhuǎn)的引導(dǎo)式訪問
      4. 多端適配:暫無進(jìn)行多端的適配測試,目前看來應(yīng)該兼容的,實用還是得做下測試進(jìn)行優(yōu)化
      5. 遮罩層:這里遮罩層做的是根據(jù)定位區(qū)域來實現(xiàn)覆蓋的,沒有進(jìn)行穿透效果,兼容可以好點,但也可以進(jìn)行其他方面的優(yōu)化例如各種形狀或者區(qū)域高亮擴展,這時候就需要更復(fù)雜的計算,擴展性維護(hù)性就差點

      優(yōu)化方向

      不足的地方都可以進(jìn)行優(yōu)化,下面就只是擴展方向:

      1. 動畫效果:為高亮區(qū)域和提示框添加過渡動畫
      2. 自動定位:通過選擇器自動獲取元素位置(使用 createSelectorQuery 和 boundingClientRect)
      3. 主題定制:支持自定義顏色和樣式
      4. 手勢支持:添加滑動手勢切換步驟
      5. 語音引導(dǎo):結(jié)合語音 API 提供語音提示
      6. 引導(dǎo)記憶:組件有個標(biāo)識專門針對已經(jīng)做過引導(dǎo)訪問的頁面進(jìn)行標(biāo)識,如果遇到可以不再引導(dǎo),也可以強制引導(dǎo)

      總結(jié)

      這個只是做了簡單的示例,有需要可以進(jìn)行優(yōu)化改善,沒有太大要求的話可以直接復(fù)制粘貼使用。效果圖片想想還是貼下吧:
      img

      posted @ 2025-07-08 10:28  幼兒園技術(shù)家  閱讀(356)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产乱老熟女乱老熟女视频| 国产毛片精品一区二区色| 亚洲av无码之国产精品网址蜜芽| 国产精品国三级国产专区| 久久久久免费看成人影片| 亚洲 欧美 影音先锋| 国产精品 亚洲一区二区三区| 国产不卡精品视频男人的天堂| 欧美乱妇高清无乱码免费| 亚洲国产av剧一区二区三区| 成A人片亚洲日本久久| 激情综合五月| 国产99在线 | 亚洲| 午夜高清福利在线观看| 久久精品伊人狠狠大香网| free性开放小少妇| 军人粗大的内捧猛烈进出视频 | 亚洲欧美中文字幕5发布| 无遮无挡爽爽免费视频| 国产三级精品三级色噜噜| 强奷乱码中文字幕| 精品国产成人国产在线视| 国产a网站| 日韩精品不卡一区二区三区| 欧美成人精品一区二区三区免费| 亚洲综合色婷婷中文字幕| 精品中文人妻在线不卡| 在线 欧美 中文 亚洲 精品| 精品国产综合一区二区三区| 亚洲人成色77777在线观看| 国产一区二区亚洲一区二区三区 | 蜜臀av久久国产午夜| 2019国产精品青青草原| 国产精品久久久久久福利69堂| 久久av无码精品人妻出轨| 亚洲成人一区| 亚洲天堂av 在线| 久久青青草原国产精品最新片 | 京山县| 久久精品国产亚洲av麻| 7m精品福利视频导航|