代码里总共包含三种需求:

1、H5弹窗往上滑动撑满屏幕效果:

效果如图:

                   →                  

大致需求是:在一个页面上,点击页面上的某个按钮,唤起弹窗。这个弹窗里面的内容有很长,可以通过滑动手机屏幕,将弹窗滑上去,使得弹窗撑满整个屏幕。该效果在地图软件比较常见,截个示例图,如下:

                                                    

2、点击按钮传递经纬度通过导航软件定位:

效果如图:

大致需求是:点击导航按钮,通过传递自己获取到的该地块的经纬度及地址名称,调用高德地图API进行定位,并打开高德地图查看该地块。

3、点击按钮传递手机号唤起拨打手机号页面效果:

效果如图:

大致需求是:点击电话按钮,并传递手机号,唤起手机号拨打电话的弹窗进行拨号。

全部代码如下:

【注:仅作自己查看和分享学习之用】

<template>
    <div id="warehouseMap" class="map__x">
        <div class="sureModule" v-if="showModule" :class="{ 'full-screen': isFullScreen }"
            :style="{ bottom: bottomPx + 'px', height: 'auto' }" @touchstart="onTouchStart" @touchmove="onTouchMove"
            @touchend="onTouchEnd">
            <template>
                <!-- 内容区域 -->
                <div class="header-content"></div>
                <!-- 可滚动区域 -->
                <div class="landInfo-bottom-container">
                    <!-- 内容XXXXXXXXXXXX -->
                </div>

                <!-- 按钮区域 -->
                <div class="ownerRowBottom-group">
                    <div @click="onNavigation()" class="ownerRow-bottom">
                        <img src="./img/daohang.png" alt="">
                        <span style="font-size: 2.9vw;color:#666666;">导航</span>
                    </div>
                    <div @click="e => telephone(e, dataObj.phone)" class="ownerRow-bottom">
                        <img src="./img/call_phone.png" alt="">
                        <span style="font-size: 2.9vw;color:#666666;">电话</span>
                    </div>
                </div>
            </template>
        </div>
    </div>
</template>

<script setup lang="ts">
import { onMounted, ref, computed } from "vue"
import * as mars3d from "mars3d"
import "ol/ol.css"
import { $message } from "@mars/components/mars-ui"
import wx from "weixin-js-sdk"
import wxWeb from "weixin-webview-jssdk"

const showModule = ref(true) // 是否显示弹窗

// 地块初始信息,可自行赋值
const dataObj = ref({
    areaName: "北京市海淀区姜州路XXXX", // 地块名称
    phone: 15355555555, // 地块电话
    lng: "116.397428", // 地块经度
    lat: "39.90923" // 地块纬度
})
const environment = ref("") // 当前处于的环境


// 弹窗相关
const windowHeight = window.innerHeight // 窗口高度
const halfHeight = windowHeight / 1.5 // 窗口高度的一小半
const topHeight = 0 // 顶部高度
const bottomPx = ref(halfHeight * -1) // 初始bottom为-half屏高度

let startY = 0 // 触摸开始时的Y坐标
let startBottom = 0 // 触摸开始时的bottom值
const isDragging = ref(false) // 是否正在拖拽
const isContentScroll = ref(false) // 是否正在滚动内容

const isFullScreen = computed(() => bottomPx.value === 0)

onMounted(() => {
    isWechatBrowser()
})

/** 触摸开始 */
function onTouchStart(e) {
    startY = e.touches[0].clientY
    startBottom = bottomPx.value
    // 判断是否在内容区
    isContentScroll.value = e.target.closest(".landInfo-bottom-container") !== null
}

/** 触摸移动 */
function onTouchMove(e) {
    const moveY = e.touches[0].clientY
    const deltaY = moveY - startY
    if (isContentScroll.value) {
    const content = document.querySelector(".landInfo-bottom-container")
    // 内容区能滚动时,不让弹窗整体滑动
    if (
      (content.scrollTop > 0 && deltaY > 0) || // 内容区已下拉
      (content.scrollTop + content.clientHeight < content.scrollHeight && deltaY < 0) // 内容区未到底部
    ) {
      // 让内容区自己滚动
      isDragging.value = false
      return
    }
    // 内容区已到顶部/底部,允许弹窗整体滑动
  }
    // 允许弹窗整体滑动
    isDragging.value = true
    let newBottom = startBottom - deltaY
    // 限制弹窗不能超出顶部和初始位置
    newBottom = Math.min(0, Math.max(newBottom, halfHeight * -1))
    bottomPx.value = newBottom
}

/** 触摸结束 */
function onTouchEnd(e) {
    // 如果不在拖拽,则不进行吸附
    if (!isDragging.value) {
    return false
    }
    // 吸附逻辑
    if (bottomPx.value > halfHeight * -0.5) {
        // 超过一半,吸附到顶部
        bottomPx.value = 0
    } else {
        // 回到半屏
        bottomPx.value = halfHeight * -1
    }
}


// 判断该H5目前是处于微信内置H5,还是被封装为微信小程序,还是纯H5
function isWechatBrowser() {
    const userAgent = navigator.userAgent.toLowerCase()
    const isWechat = userAgent.includes("micromessenger") // ios的userAgent中无miniProgram,但都有MicroMessenger(表示是微信浏览器)

    if (!isWechat) {
        environment.value = "H5" // 否则,当前在标准 H5 环境中
        return false
    }
    // 判断是否处于微信环境
    wxWeb.miniProgram.getEnv((res: any) => {
        if (res.miniprogram) { // 检查是否在微信小程序环境中
            environment.value = "WeChatMiniProgram"
        } else { // 在微信 H5 环境中(在微信浏览器中)
            environment.value = "WeChatH5"
        }
    })
}


/**
 * 打开高德地图
 * lng:经度,lat:纬度,addr:地址名称
 * @param positionObj:定位的信息
 */
function openNavMap(positionObj: any) {
    // 将标准无偏坐标(WGS84)转为国测局坐标
    const positions = mars3d.PointTrans.wgs2gcj([positionObj.lng, positionObj.lat])

    const lat = positions[1]
    const lng = positions[0]
    const title = positionObj.addr || ""

    const url = `https://uri.amap.com/marker?position=${lng},${lat}&name=${title}&coordinate=gaode&callnative=1`
    if (window.plus) {
        window.plus.runtime.openURL(url)
    } else {
        location.assign(url)
    }
}


/** 导航 */
const onNavigation = () => {
    // 传入经纬度,跳转地图
    if (environment.value === "WeChatMiniProgram") { // 微信小程序
        // “/pages/map/index”是由本H5封装的小程序那边的页面路径,需要根据实际情况修改
        wx.miniProgram.navigateTo({
            url: `/pages/map/index?lng=${Longitudedu.value}&lat=${Latitudedu.value}&address=${dataInfo.value?.areaCodeName}`
        })
    } else {
        openNavMap({ lng: dataObj.value.lng, lat: dataObj.value.lat, addr: dataObj.value?.areaName })
    }
}

/**
 * 拨打电话
 * @param event
 * @param phone
 */
const telephone = (event: any, phone: number) => {
    if (!phone) {
        $message("暂无手机号信息")
        return false
    }
    event.stopPropagation()
    window.location.href = `tel:` + phone
}
</script>

<style lang="less" scoped>
.map__x {
    width: 100vw;
    height: 100vh;
    letter-spacing: 0.4vw;

    .sureModule.full-screen {
        top: 0;
        bottom: 0 !important;
        height: 100vh !important;
        border-radius: 0 !important;
    }

    .sureModule {
        position: fixed;
        left: 0;
        width: 100vw;
        z-index: 999;
        transition: transform 0.3s cubic-bezier(.25, .8, .25, 1); //动画效果
        transform: translateY(var(--popup-translate, 0));
        z-index: 999;
        box-sizing: border-box;
        width: 100vw;
        background-color: #fff;
        border-top-left-radius: 1vw;
        border-top-right-radius: 1vw;

        .landInfo-bottom-container {
            flex: 1 1 0%;
            min-height: 0;
            overflow-y: auto;
            -webkit-overflow-scrolling: touch;
            margin-bottom: 10vw;

            .landInfo-bottom-top {
                  padding: 0 5vw;
                  margin-bottom: 0.5vw;
            }

            .segLine {
                  width: 100%;
                  height: 2vw;
                  background-color: #F6F7F8;
            }
      }
    }
}
</style>

如果需要小程序封装H5调起腾讯导航,则小程序项目这边需要加个map/index的页面。且需要在微信公众平台——小程序开发那里,开通小程序wx.getLoction接口,在index/index页面加上wx.getLocation唤起位置弹窗,获取本地位置。(以下是uniapp的写法)

index/index.vue

<template>
	<view class="container">
		<web-view :src="urlWeb"></web-view>
	</view>
</template>
<script>
	export default {
		data() {
			return {
				urlWeb: "https://xxxxx.cn",
				latitude: "",
				longitude: ""
			}
		},
		onLoad(options) {
			// 用户已经同意授权
			uni.getLocation({
				type: "wgs84", // 默认为 wgs84,gcj02 为火星坐标系
				isHighAccuracy: true,//是否开启高精度定位
				highAccuracyExpireTime: 3500,//高精度定位超时时间
				success: function(res) {
					// res.latitude: 纬度
					// res.longitude: 经度
					// res.speed: 速度
					// res.accuracy: 位置精确度
					// res.altitude: 高度
					// res.verticalAccuracy: 垂直精确度
					// res.horizontalAccuracy: 水平精确度
					this.latitude = res.latitude // 纬度,浮点数,范围为90 ~ -90
					this.longitude = res.longitude // 经度,浮点数,范围为180 ~ -180。
				},
				fail: function(err) {
					console.error('获取位置失败:', err)
				},
				cancel: function() {
					console.log("用户拒绝授权获取地理位置")
				}
			})
			this.urlWeb =
				`https://xxxxxxxxxxx.cn?latitude=${this.latitude}&longitude=${this.longitude}&timestamp=${Math.random()}`
		},
	}
</script>

<style>
	
</style>

在 app.json 中配置位置权限:

{
  "pages": [
    "pages/index/index",
    "pages/map/index"
  ],
  "window": {
    // 自己的配置
  },
  "requiredPrivateInfos":["getLocation"],//声明小程序需要使用的隐私接口
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },

  //以下可选
  "rendererOptions": {
    //启用 Skyline 渲染引擎,相比传统 WebView 渲染,性能提升明显
    "skyline": {
      "defaultDisplayBlock": true,//设置默认的显示模式为块级元素
      "disableABTest": true,//禁用 A/B 测试,当设置为 true 时,微信不会对当前小程序进行 A/B 测试,确保渲染行为的一致性
      "sdkVersionBegin": "3.0.0",//设置支持 Skyline 的最低微信版本,只有微信版本 >= 3.0.0 的用户才能使用 Skyline 渲染引擎
      "sdkVersionEnd": "15.255.255",//设置支持 Skyline 的最高微信版本
    }
  },
  "componentFramework": "glass-easel",//启用 glass-easel 组件框架,组件渲染和更新更高效,减少组件异常
  "sitemapLocation": "sitemap.json",//指定小程序站点地图文件的位置,告诉微信小程序引擎在哪里找到 sitemap.json 文件
  "lazyCodeLoading": "requiredComponents",//启用按需加载组件
  "globalData": { //定义全局数据,在 globalData 中定义的数据可以在整个小程序中访问
    "messageEvent": null
  }
}

map/index.vue

<template>
	<view>

	</view>
</template>

<script>
	export default {
		data() {
			return {
				isReturned: false
			}
		},
		onLoad(option) {
			const {
				lat,
				lng,
				address
			} = option || {}
            // 唤起导航
			uni.openLocation({
				latitude: Number(lat),
				longitude: Number(lng),
				name: address,
				address: address,
				complete: () => {
					//地图关闭后
					if (!this.isReturned) {
						this.isReturned = true
					}
				}
			})
		},
		onShow() {
			if (this.isReturned) {
                // 回到上一页面
				uni.navigateBack();
			}
		}
	}
</script>

<style>

</style>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐