uni-app 当滚动区域的高度大于等于视口高度(100%或100vh)时 弹出层中的canvas绘制错位

uni-app 当滚动区域的高度大于等于视口高度(100%或100vh)时 弹出层中的canvas绘制错位

示例代码:

<template>  
    <view class="app-container">  
        <view v-for="item in 20" :key="item" class="warp-item" @click="open">  
            占位元素  
        </view>  
        <uni-popup ref="popup" type="bottom" background-color="#FFFFFF">  
            <canvas canvas-id="my-canvas" class="my-canvas"></canvas>  
        </uni-popup>  
    </view>  
</template>  

<script>  
export default {  
    data() {  
        return {  
            title: 'Hello'  
        }  
    },  
    onLoad() {  
        const context = uni.createCanvasContext("my-canvas", this)  
        context.setStrokeStyle("#00ff00")  
        context.setLineWidth(5)  
        context.rect(0, 0, 200, 200)  
        context.stroke()  
        context.setStrokeStyle("#ff0000")  
        context.setLineWidth(2)  
        context.moveTo(160, 100)  
        context.arc(100, 100, 60, 0, 2 * Math.PI, true)  
        context.moveTo(140, 100)  
        context.arc(100, 100, 40, 0, Math.PI, false)  
        context.moveTo(85, 80)  
        context.arc(80, 80, 5, 0, 2 * Math.PI, true)  
        context.moveTo(125, 80)  
        context.arc(120, 80, 5, 0, 2 * Math.PI, true)  
        context.stroke()  
        context.draw()  
    },  
    methods: {  
        open() {  
            this.$refs.popup.open()  
        }  
    }  
}  
</script>  

<style scoped lang="scss">  
.app-container {  
    height: 100vh;  
    background-color: #CCCCCC;  
    overflow-y: auto;  

    .warp-item {  
        height: 100rpx;  
        background-color: #FFFFFF;  
        margin-bottom: 10rpx;  
        display: flex;  
        justify-content: center;  
        align-items: center;  
    }  

    .my-canvas {  
        width: 100%;  
        height: 500rpx;  
    }  
}  
</style>

操作步骤:

滑动列表,滚动到中间或底部,然后点击占位元素,弹窗中的canvas绘制出现偏移,只在真机中存在问题,修改.app-container的高度为height: calc(100vh - 1px);即可解决问题,不知道是否属于bug

预期结果:

滑动列表,滚动到中间或底部,然后点击占位元素,弹窗中的canvas绘制出现偏移,只在真机中存在问题,修改.app-container的高度为height: calc(100vh - 1px);即可解决问题,不知道是否属于bug

实际结果:

滑动列表,滚动到中间或底部,然后点击占位元素,弹窗中的canvas绘制出现偏移,只在真机中存在问题,修改.app-container的高度为height: calc(100vh - 1px);即可解决问题,不知道是否属于bug

bug描述:

当滚动区域的高度大于等于视口高度(100%或100vh),滚动列表后,popup组件中的canvas绘制出现了偏移,且只在真机出现,开发者工具正常展示,修改.app-container的高度为height: calc(100vh - 1px);即可解决问题,不知道是否属于bug

image


更多关于uni-app 当滚动区域的高度大于等于视口高度(100%或100vh)时 弹出层中的canvas绘制错位的实战教程也可以访问 https://www.itying.com/category-93-b0.html

5 回复

我测试了你提供的代码,的确有这个 canvas 漂移的问题,真机调试发现 canvas 元素本身的位置是正确的,但实际回执的位置不对。
使用新版本的 canvas 写法,弹窗后重新绘制,位置是正常的,怀疑是微信旧版的 canvas 处理不当。

更多关于uni-app 当滚动区域的高度大于等于视口高度(100%或100vh)时 弹出层中的canvas绘制错位的实战教程也可以访问 https://www.itying.com/category-93-b0.html


好的,感谢反馈,我这边试一下新版写法看看

这是bug复现的代码包

感谢反馈。我验证下你提供的代码

在使用 uni-app 开发时,如果在滚动区域的高度大于或等于视口高度(即 100%100vh)的情况下,弹出层中的 canvas 绘制出现错位问题,通常是由于 canvas 的绘制位置或尺寸计算不准确导致的。以下是一些可能的解决方案:


1. 检查 canvas 的尺寸和位置

确保 canvas 的尺寸和位置是相对于弹出层的视口计算的,而不是相对于页面的滚动区域。可以使用 uni-app 提供的 getSystemInfoSync 获取视口高度,并根据需要动态设置 canvas 的尺寸。

const systemInfo = uni.getSystemInfoSync();
const canvasHeight = systemInfo.windowHeight; // 视口高度
const canvasWidth = systemInfo.windowWidth; // 视口宽度

canvasstyle 中动态设置高度和宽度:

<canvas canvas-id="myCanvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>

2. 使用 fixed 定位弹出层

如果弹出层是浮动的,确保其使用 fixed 定位,而不是 absolute 定位。这样可以避免滚动区域的影响。

.popup {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  z-index: 999;
}

3. 监听滚动事件并重新绘制

如果滚动区域的高度大于视口高度,可能会导致 canvas 的绘制位置错位。可以通过监听滚动事件,在滚动时重新计算 canvas 的绘制位置。

onPageScroll(e) {
  this.scrollTop = e.scrollTop; // 获取滚动距离
  this.redrawCanvas(); // 重新绘制 canvas
},
methods: {
  redrawCanvas() {
    const ctx = uni.createCanvasContext('myCanvas', this);
    // 根据 scrollTop 重新计算绘制位置
    ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    ctx.draw();
  }
}

4. 使用 uni.createSelectorQuery 获取元素位置

通过 uni.createSelectorQuery 获取 canvas 或相关元素的位置信息,确保绘制时使用正确的坐标。

uni.createSelectorQuery()
  .select('#myCanvas')
  .boundingClientRect((rect) => {
    const canvasTop = rect.top; // canvas 距离视口顶部的距离
    const canvasLeft = rect.left; // canvas 距离视口左边的距离
    // 根据 canvasTop 和 canvasLeft 进行绘制
  })
  .exec();

5. 避免在滚动区域中使用 canvas

如果可能,尽量避免在滚动区域中使用 canvas,尤其是在弹出层中。可以将 canvas 放置在固定位置,或者使用其他方式(如图片)替代 canvas 的绘制。


6. 调试和排查

  • 使用 console.log 输出 canvas 的尺寸、位置以及绘制时的坐标,检查是否存在偏差。
  • 使用 uni-app 的调试工具检查 canvas 的实际渲染情况。

示例代码

以下是一个完整的示例,结合了上述解决方案:

<template>
  <view>
    <!-- 滚动区域 -->
    <scroll-view scroll-y style="height: 100vh;">
      <view style="height: 150vh; background: #f0f0f0;">
        <!-- 内容 -->
      </view>
    </scroll-view>

    <!-- 弹出层 -->
    <view class="popup" v-if="showPopup">
      <canvas canvas-id="myCanvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      showPopup: true,
      canvasWidth: 0,
      canvasHeight: 0,
    };
  },
  mounted() {
    const systemInfo = uni.getSystemInfoSync();
    this.canvasWidth = systemInfo.windowWidth;
    this.canvasHeight = systemInfo.windowHeight;
    this.drawCanvas();
  },
  methods: {
    drawCanvas() {
      const ctx = uni.createCanvasContext('myCanvas', this);
      ctx.fillStyle = 'red';
      ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
      ctx.draw();
    },
  },
};
</script>

<style>
.popup {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.5);
  z-index: 999;
}
</style>
回到顶部