【小程序开发】地图circle自适应大小(radius适配)

问题

小程序地图组件map可以配置circles,用于标示当前位置的一个范围
circle有一个字段radius来规定圆圈半径
不过,这个值是与地图对应,即地图缩放时,它并不会改变大小

circle的透明度是通过配置fillColor属性的#XXXXXX00最后2位实现的,当不配置(即颜色值只有6位)时等同于opacity=1,当为00时,等同于opacity=0,以此类推。

在这里插入图片描述

官网api:

circles 在地图上显示圆

属性说明类型必填备注
latitude纬度Number浮点数,范围 -90 ~ 90
longitude经度Number浮点数,范围 -180 ~ 180
color描边的颜色String十六进制
fillColor填充颜色String十六进制
radius半径Number
strokeWidth描边的宽度Number

这里的radius就是来表示圆的半径的

解决方案

比例尺

最开始是打算通过地图比例尺,然后就可以计算出来了,结果,官方论坛
在这里插入图片描述
emmmm,算了,另外想方案= =

是时候考验真正的技术了

官方有个接口
MapContext.getRegion(Object object)
基础库 1.4.0 开始支持,低版本需做兼容处理。

获取当前地图的视野范围

参数
Object object

属性类型默认值必填说明
successfunction接口调用成功的回调函数
failfunction接口调用失败的回调函数
completefunction接口调用结束的回调函数(调用成功、失败都会执行)

object.success 回调函数
参数

Object res

属性类型说明
southwestnumber西南角经纬度
northeastnumber东北角经纬度

通过这个接口可以获取到2个坐标(左下角和右上角)
这样,我们就可以通过这2个坐标计算出2点之间的水平/垂直距离
从而得到radius的值

radius值为number,而且根据观察应该是1代表1m

计算2坐标换算距离我是参考微信小程序中使用地图和定位的一些坑和经验中的计算方法

var EARTH_RADIUS = 6378.137; //地球半径
function rad(d) {
    return d * Math.PI / 180.0;
}
function getDistance(lng1, lat1, lng2, lat2) {
  var radLat1 = rad(lat1);
  var radLat2 = rad(lat2);
  var a = radLat1 - radLat2;
  var b = rad(lng1) - rad(lng2);
  var s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
    + Math.cos(radLat1) * Math.cos(radLat2)
    * Math.pow(Math.sin(b / 2), 2)));
  s = s * EARTH_RADIUS;
  s = Math.round(s * 10000) / 10000;
  return s;//返回数值单位:公里
}

通过getRegion接口我们得到2个坐标值,通过计算判断,得到短边,然后进行计算短边实际距离

this.wxmap.getRegion({
   success:res=>{
       let lng1 = res.northeast.longitude;
       let lat1 = res.northeast.latitude;
       let lng2 = res.southwest.longitude;
       let lat2 = res.southwest.latitude;

       let longitude = lng1 - lng2;
       let latitude = lat1 - lat2;
       let flag = longitude>latitude?true:false;
       let radius = 0;
       //计算得到短边,然后再通过*1000转变为m,除2得到半径,*0.8优化显示,让圈圈只占界面的80%
       if(flag){
           radius= app.getDistance(lng1,lat1,lng1,lat2)*1000/2*0.8;
       }else{
           radius= app.getDistance(lng1,lat1,lng2,lat1)*1000/2*0.8;
       }
       this.setData({
           "circles[0].radius":radius
       });
   }
});

这样,所需要的效果就达到了。

<think>好的,我现在要帮用户封装一个微信小程序的环形进度条组件,只需要传入百分比参数。首先,我得回顾一下用户之前的问题,他之前问过父组件控制子组件显示隐藏的问题,现在这个进度条组件应该也是类似的组件封装,不过这次是环形进度条。 用户的需求很明确,就是创建一个组件,父组件传入百分比,子组件显示对应的环形进度。我需要考虑如何用微信小程序的组件化方式来实现,可能要用到WXML的canvas或者SVG,不过小程序对SVG的支持可能有限,所以可能用canvas来绘制环形。 首先,组件的结构。需要创建一个组件,比如叫circle-progress,然后在组件的properties里定义一个percent属性,类型是Number,默认值0。这样父组件就可以通过percent属性传入百分比了。 接下来,绘制环形进度条。考虑到性能,使用canvas可能比较合适。但是canvas在小程序组件中的使用需要注意,每个组件内的canvas需要指定类型为2d,并且要在组件初始化时获取上下文。不过,可能需要在组件加载后,也就是attached生命周期里初始化canvas,然后根据percent的变化重新绘制。 然后,绘制环形需要计算角度,根据百分比转换成弧度。可能需要用到arc方法,设置起始和结束角度。同时,可以自定义颜色,比如进度条的颜色、背景色、文字颜色等,这些可能需要作为可配置的属性,但用户目前只需要传入百分比,所以可能先固定颜色,或者提供默认值,后面再扩展。 另外,可能需要显示百分比文字在中间,所以需要在canvas上绘制文本。文字的位置要居中,字体大小和颜色也需要考虑。 还要考虑组件的宽高,比如设置一个默认的宽度和高度,比如200px,或者让父组件通过样式传入。但用户可能希望组件自适应,所以最好在组件内部固定一个大小,或者在properties里添加size属性来控制。 接下来,代码结构。组件的wxml里需要一个canvas元素,指定type为2d,并且设置一个id,比如"progressCanvas"。然后在js里获取这个canvas节点,调用wx.createCanvasContext或者新的api,但要注意小程序的兼容性。 绘制过程包括:绘制底层背景圆环,绘制当前进度的圆环,绘制中间的百分比文字。每次percent变化时,需要重新绘制。所以需要在observers里监听percent的变化,触发重绘。 可能遇到的问题:canvas在组件中的层级问题,是否会被其他元素覆盖;动态更新时的性能问题,是否需要使用requestAnimationFrame;百分比的小数处理,比如传入的是50,显示50%。 另外,用户可能需要动画效果,比如进度条从0逐渐增加到目标百分比,这可能需要使用定时器逐步增加percent的值,但用户当前的需求只是传入百分比参数,是否需要动画可能要看用户是否需要,但用户没提,可能先不做,或者作为可配置选项。 测试一下,组件在父组件中引入,传入percent,是否能正确显示环形进度。如果遇到不更新的问题,可能需要检查是否在setData后正确触发了重绘。 总结步骤: 1. 创建组件circle-progress 2. 定义percent属性 3. 在attached生命周期初始化canvas 4. 绘制背景圆环和当前进度 5. 监听percent变化,重新绘制 6. 处理文字显示 7. 提供默认样式和可配置选项</think>以下是在微信小程序中封装环形进度条组件的完整实现方案,支持自定义颜色、尺寸和动态百分比: --- ### 一、组件代码实现 #### 1. 组件文件结构 ``` components/ └─ circle-progress/ ├─ circle-progress.js // 组件逻辑 ├─ circle-progress.json // 组件声明 ├─ circle-progress.wxml // 组件模板 └─ circle-progress.wxss // 组件样式 ``` #### 2. 组件配置文件 ```json // circle-progress.json { "component": true, "usingComponents": {} } ``` #### 3. 组件逻辑 ```javascript // circle-progress.js Component({ properties: { percent: { // 进度百分比(0-100) type: Number, value: 0, observer: 'draw' // 值变化时触发重绘 }, size: { // 画布尺寸(单位px) type: Number, value: 200 }, lineWidth: { // 进度条宽度 type: Number, value: 10 }, activeColor: { // 进度条颜色 type: String, value: '#07c160' }, backgroundColor: { // 背景色 type: String, value: '#f3f3f3' } }, data: { ctx: null, // 画布上下文 canvasId: 'progressCanvas' }, lifetimes: { ready() { this.initCanvas() } }, methods: { // 初始化画布 initCanvas() { const { size } = this.data this.setData({ ctx: wx.createCanvasContext(this.data.canvasId, this) }) this.draw() }, // 绘制进度条 draw() { const { ctx, size, lineWidth, percent, activeColor, backgroundColor } = this.data const radius = size / 2 - lineWidth const cx = size / 2 const cy = size / 2 const startAngle = -Math.PI / 2 const endAngle = startAngle + (Math.PI * 2 * percent) / 100 // 清空画布 ctx.clearRect(0, 0, size, size) // 绘制背景圆环 ctx.setLineWidth(lineWidth) ctx.setStrokeStyle(backgroundColor) ctx.arc(cx, cy, radius, 0, Math.PI * 2) ctx.stroke() // 绘制进度圆环 ctx.beginPath() ctx.setStrokeStyle(activeColor) ctx.arc(cx, cy, radius, startAngle, endAngle) ctx.stroke() // 绘制百分比文字 ctx.setFontSize(20) ctx.setFillStyle('#333') ctx.setTextAlign('center') ctx.setTextBaseline('middle') ctx.fillText(`${percent}%`, cx, cy) // 提交绘制 ctx.draw() } } }) ``` #### 4. 组件模板 ```wxml <!-- circle-progress.wxml --> <view class="container"> <canvas class="canvas" canvas-id="{{canvasId}}" style="width: {{size}}px; height: {{size}}px" ></canvas> </view> ``` #### 5. 组件样式 ```wxss /* circle-progress.wxss */ .container { position: relative; display: inline-block; } .canvas { display: block; } ``` --- ### 二、使用示例 #### 1. 在父组件注册 ```json { "usingComponents": { "circle-progress": "/components/circle-progress/circle-progress" } } ``` #### 2. 在页面中使用 ```wxml <!-- 基础用法 --> <circle-progress percent="{{75}}" /> <!-- 自定义样式 --> <circle-progress percent="{{progressValue}}" size="300" line-width="15" active-color="#1890ff" backgroundColor="#e8e8e8" /> ``` #### 3. 动态更新进度 ```javascript Page({ data: { progressValue: 0 }, onLoad() { // 模拟进度变化 setInterval(() => { if(this.data.progressValue < 100) { this.setData({ progressValue: this.data.progressValue + 1 }) } }, 50) } }) ``` --- ### 三、核心实现原理 1. **Canvas绘制机制**: - 使用`wx.createCanvasContext`创建2D绘图上下文 - 通过`arc`方法绘制两个同心圆弧: - 底层灰色圆弧表示总进度 - 上层彩色圆弧表示当前进度 - 数学公式:$$ \text{结束角度} = -\frac{\pi}{2} + 2\pi \times \frac{\text{percent}}{100} $$ 2. **性能优化方案**: ```javascript // 在组件中增加防抖处理 const debounce = (func, delay = 16) => { let timer return function(...args) { clearTimeout(timer) timer = setTimeout(() => { func.apply(this, args) }, delay) } } // 修改draw方法 draw: debounce(function() { // 原绘制逻辑 }) ``` --- ### 四、扩展功能建议 1. **动画支持**: ```javascript Component({ // ... methods: { animateTo(targetPercent, duration = 1000) { const start = Date.now() const startPercent = this.data.percent const step = () => { const now = Date.now() const progress = Math.min((now - start) / duration, 1) const current = startPercent + (targetPercent - startPercent) * progress this.setData({ percent: Math.round(current) }) if(progress < 1) { requestAnimationFrame(step) } } requestAnimationFrame(step) } } }) ``` 2. **多色渐变支持**: ```javascript // 修改绘制逻辑 const gradient = ctx.createLinearGradient(0, 0, size, 0) gradient.addColorStop(0, '#ff0000') gradient.addColorStop(1, '#00ff00') ctx.setStrokeStyle(gradient) ``` --- ### 五、常见问题解决方案 1. **Canvas模糊问题**: - 添加高清屏适配代码: ```javascript const dpr = wx.getSystemInfoSync().pixelRatio ctx.scale(dpr, dpr) ``` 2. **动态尺寸适配**: ```javascript Component({ // ... methods: { updateSize() { const query = this.createSelectorQuery() query.select('.container').boundingClientRect(res => { this.setData({ size: res.width }) this.draw() }).exec() } } }) ``` 3. **组件复用冲突**: - 动态生成canvasId: ```javascript data: { canvasId: `canvas_${Math.random().toString(36).substr(2)}` } ``` --- **最终效果**: - 输入参数:`percent`(必需) - 可选参数:`size`/`lineWidth`/`activeColor`/`backgroundColor` - 支持动态更新进度 - 自动适应高清屏幕 - 默认包含百分比文字显示 通过这个组件封装,可以实现灵活的环形进度展示效果,适用于文件上传、任务进度等场景。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值