解决高德地图因被transform缩放导致获取鼠标点击地图某点的经纬度不准问题

本文介绍了如何使用React实现大屏窗口缩放适配,并结合高德地图API创建地图组件。在缩放过程中遇到地图标记点位置偏差的问题,通过分析原因并提出解决方案,最终成功实现在鼠标点击地图时精确树立标记点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文完整demo在下面。

大屏

在做大屏的时候,为了保证大屏完整的呈现在窗口中,一种简单的做法是大屏尺寸根据窗口尺寸做缩放调整,就像这样:
大屏跟随屏幕尺寸变化缩放演示
想实现上面这种效果,非常容易,监听window的resize事件,当window的resize事件触发时,根据此时window的尺寸与大屏的设计尺寸计算出一个缩放值,将大屏按照此缩放值进行transform:scale缩放。下面我用react简单做一个:
App.jsx

// App.jsx

import Screen from './components/Screen'

export default function(){
  return <Screen desginWidth={1920} desginHeight={1080}>
      <div>大屏区域</div>
	</Screen> 
}

Screen.jsx

// Screen.jsx

import { useState, useEffect } from "react"

// 大屏容器样式
const style = {
  position: "absolute",
  left: "50%",
  top: "50%",
  backgroundColor: 'red'
}

// 生成一个防抖函数
function debounce(callback, time){
  let timer = null 
  return function(...arg){
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      callback(...arg)
    }, time)
  } 
}

// 计算缩放值
function calculate(desginWidth, desginHeight){
  const { innerWidth, innerHeight } = window
  if(innerWidth / innerHeight < desginWidth / desginHeight) {
    return innerWidth / desginWidth
  }
  return innerHeight / desginHeight
}

// props: { designWidth:number, desginHeight:number }
export default (props) => {

  // 缩放值
  const [scale, setScale] = useState(1)

  // 组件首次加载时执行的逻辑
  useEffect(() => {
  
    // 当窗口触发resize事件时的回调函数
    const resizeHandler = debounce(() => {
      setScale(calculate(props.desginWidth, props.desginHeight))
    }, 300)
    
	// 注册窗口的resize事件
    window.addEventListener('resize', resizeHandler)
    
    resizeHandler()
    
    // 当组件卸载时移除之前窗口注册的resize事件
    return () => { window.removeEventListener('resize', resizeHandler) }
  }, [])

  return <div 
    style={{
      ...style, 
      width: `${props.desginWidth}px`,
      height: `${props.desginHeight}px`,
      transform:`translate(-50%, -50%) scale(${scale})`
    }}
  >
    {props.children}
  </div>
}

地图

大屏组件有了,接下来利用高德地图api封装一个地图组件:
Map.jsx

// Map.jsx

import AMapLoader from '@amap/amap-jsapi-loader'
import { useRef } from 'react'

const style = {
  width: "100%",
  height: "100%"
}

export default () => {
  const map = useRef(null)
  
  AMapLoader.load({
    key: "2a49071d959081b738749e17f8207278",
  }).then(() => {
    const {AMap} = window
    
    // 创建一个地图
    map.current = new AMap.Map('container', {
      center: [105.602725,37.076636],
    })
  })

  return <div id='container' style={style}></div>
}

将地图组件安置到大屏里:
App.jsx

// App.jsx
import Screen from './components/Screen'
import Map from './components/Map'

export default function(){
  return <Screen desginWidth={1920} desginHeight={1080}>
      <Map />
   </Screen> 
}

到目前,效果是这样的:
地图效果
接下来实现鼠标点击地图某点就在该点树立一个标记点的功能。监听地图点击事件,事件参数中会有鼠标点击点的经纬度,高德地图api提供了点标记功能,能根据经纬度在地图上树立标记点。
在这里插入图片描述
试试效果:
请添加图片描述
期望的效果是在鼠标点击处产生标记点,但是实际上标记点的位置离鼠标点击位置有很大的偏差。

产生问题的原因

下图这个是大屏缩放正好是1的情况【transform:scale(1) 】,这个五角星的像素坐标是(784,321)。
在这里插入图片描述
如果将窗口缩小,大屏也会跟着缩小:
在这里插入图片描述比如上图这样,此时的大屏缩放变成0.8286792452830188的情况【transform: scale(0.8286792452830188)】,五角星的像素坐标是(652,266)。你会发现像素坐标除以缩放值得到的是大屏缩放为1的情况【transform:scale(1)】的像素坐标,比如652除以0.8286792452830188得到的786,266除以0.8286792452830188得到320(计算结果有轻微偏差,因为有小数除不尽,以及我测量的也不是很准确导致)。

鼠标点击点的经纬度应该来自大屏缩放为1的情况【transform: scale(1)】的像素坐标。所以你需要将得到的像素坐标除以缩放值后再转换成经纬度,再根据该经纬度立标记点,具体实施步骤见下面的实施解决方案。

实施解决方案

通过点击地图事件的事件参数除了能拿到点击地图点的经纬度外,还可以拿到点击地图点的像素坐标,将像素坐标除以大屏目前的缩放值会得到一个新的坐标,将这个新的坐标转换成对应的经纬度(通过地图实例身上的containerToLngLat函数)作为标记点的经纬度。
在这里插入图片描述
试试效果:
请添加图片描述
nice

完整demo

/ -
  - App.jsx
  - components
  	 - Screen.jsx
  	 - Map.jsx
   - node_modules
   - package.json

Screen.jsx:

import { useState, useEffect, createContext } from "react"

export const context = createContext({
  scale: 1
})

// 大屏容器样式
const style = {
  position: "absolute",
  left: "50%",
  top: "50%",
  backgroundColor: 'red'
}

// 生成一个防抖函数
function debounce(callback, time){
  let timer = null 
  return function(...arg){
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
      callback(...arg)
    }, time)
  } 
}

// 计算缩放值
function calculate(desginWidth, desginHeight){
  const { innerWidth, innerHeight } = window
  if(innerWidth / innerHeight < desginWidth / desginHeight) {
    return innerWidth / desginWidth
  }
  return innerHeight / desginHeight
}

// props: { designWidth:number, desginHeight:number }
export default (props) => {

  // 缩放值
  const [scale, setScale] = useState(1)

  // 组件首次加载时执行的逻辑
  useEffect(() => {
  
    // 当窗口触发resize事件时的回调函数
    const resizeHandler = debounce(() => {
      setScale(calculate(props.desginWidth, props.desginHeight))
    }, 300)
    
	// 注册窗口的resize事件
    window.addEventListener('resize', resizeHandler)
    
    resizeHandler()
    
    // 当组件卸载时移除之前窗口注册的resize事件
    return () => { window.removeEventListener('resize', resizeHandler) }
  }, [])

  return <div 
    style={{
      ...style, 
      width: `${props.desginWidth}px`,
      height: `${props.desginHeight}px`,
      transform:`translate(-50%, -50%) scale(${scale})`
    }}
  >
    <context.Provider value={{ scale }}>
      {props.children}
    </context.Provider>
  </div>
}

Map.jsx:

import AMapLoader from '@amap/amap-jsapi-loader'
import { useRef, useContext } from 'react'
import { context } from '../components/Screen'

const style = {
  width: "100%",
  height: "100%",
}

export default () => {
  const map = useRef(null)
  const {scale} = useContext(context)
  AMapLoader.load({
    key: "2a49071d959081b738749e17f8207278",
  }).then(() => {
    const {AMap} = window

    // 创建一个地图
    map.current = new AMap.Map('container', {
      center: [105.602725,37.076636],
    })

    //监听点击地图事件
    map.current.on('click', function(e) {

      // 鼠标点击地图点的像素坐标
      const {x, y} = e.pixel

      // 像素坐标除以大屏缩放值得到一个新的坐标
      const pixel2 = new AMap.Pixel(x / scale, y / scale)

      // 新的坐标转换成对应的经纬度
      const {lng, lat} = map.current.containerToLngLat(pixel2)
      
      // 创建点标记
      const marker = new AMap.Marker({
        position: new AMap.LngLat(lng,lat),
        title: "一个标记点"
      })

      // 将点标记添加到地图上
      map.current.add(marker)
    })

  })

  return <div id='container' style={style}></div>
}

App.jsx:

import Screen from './components/Screen'
import Map from './components/Map'

export default function(){
  return <Screen desginWidth={1920} desginHeight={1080}>
      <Map />
  </Screen> 
}
大屏可视化适配flexible.js是一种通过使用CSS的transform属性来实现大屏幕适配的方法。其主要思路是通过计算固定的宽高比例,将大屏幕容器设置为一个参考元素,然后监听浏览器窗口的resize事件,在每次窗口大小改变后重新计算宽度和高度的比例。根据窗口的长宽比与固定的长宽比进行比较,如果窗口的长宽比更长,则根据窗口的高度值重新计算缩放比例,并使用transformscale属性对大屏幕容器进行缩放操作,同时使用translate属性将容器移到窗口的中央。同理,如果窗口的长宽比更短,则根据窗口的长度值计算缩放比例,并对大屏幕容器进行相应的缩放操作。通过这种方式,可以实现大屏幕的适配。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [Vue项目大屏可视化适配 transform+解决高德地图经纬度偏移](https://blog.csdn.net/qq_45642765/article/details/125988515)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Vue 可视化大屏适配方案](https://blog.csdn.net/Windyluna/article/details/122296831)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值