<template>
<view>
<button type="primary" @click="chooseImage">选择图片</button>
<canvas canvas-id="canvas" ref="canvas" class="canvas" :data-image="imageSrc" :start="startStatus" :change:start="animate.start"
:touch="touchOperation" :change:touch="animate.onMessage" :data-width="canvasWidth"
:data-height="canvasWidth" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd" />
</view>
</template>
<script module="animate" lang="renderjs">
function Ball({
x,
y,
vx,
vy,
canvasWidth,
canvasHeight,
ctx
}) {
this.x = x
this.y = y
this.vx = vx
this.vy = vy
this.canvasWidth = canvasWidth
this.canvasHeight = canvasHeight
this.ctx = ctx
this.radius = 5
}
Ball.prototype.draw = function() {
this.ctx.beginPath()
this.ctx.fillStyle = '#007AFF'
this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
this.ctx.closePath()
this.ctx.fill()
}
Ball.prototype.move = function() {
this.x += this.vx
this.y += this.vy
if (this.x < this.radius || this.x > this.canvasWidth - this.radius) {
this.vx *= -1
}
if (this.y < this.radius || this.y > this.canvasHeight - this.radius) {
this.vy *= -1
}
}
function getDistance(x, y) {
return Math.sqrt(x * x + y * y)
}
export default {
mounted() {
this.dragging = false
this.imgState = {
x: 50,
y: 50,
width: 100,
height: 100,
offsetX: 0,
offsetY: 0
}
},
methods: {
start(newVal, oldVal, owner, ins) {
const canvasWidth = ins.getDataset().width
const canvasHeight = ins.getDataset().height
const canvasEle = document.querySelectorAll('.canvas>canvas')[0]
const ctx = canvasEle.getContext('2d')
const ballList = []
const speed = 3
const layer = 3
const ballInlayer = 20
for (let i = 0; i < layer; i++) {
const radius = getDistance(canvasWidth / 2, canvasHeight / 2) / layer * i
for (let j = 0; j < ballInlayer; j++) {
const deg = j * 2 * Math.PI / ballInlayer
const x = radius * Math.cos(deg) + canvasWidth / 2
const y = radius * Math.sin(deg) + canvasHeight / 2
const vx = speed * Math.cos(deg)
const vy = speed * Math.sin(deg)
ballList.push(new Ball({
x,
y,
vx,
vy,
canvasWidth,
canvasHeight,
ctx
}))
}
}
const image = new Image()
const imageSrc = ins.getDataset().image || 'static/test/blurred_transparent_image.png'
image.src = imageSrc
image.onload = () => {
const drawAll = () => {
ctx.clearRect(0, 0, canvasWidth, canvasHeight)
ballList.forEach(ball => {
ball.move()
ball.draw()
})
ctx.drawImage(image, this.imgState.x, this.imgState.y, this.imgState.width, this.imgState
.height)
requestAnimationFrame(drawAll)
}
drawAll()
}
},
onMessage(newVal, owner, ins) {
const data = newVal
const canvasEle = document.querySelectorAll('.canvas>canvas')[0]
const rect = canvasEle.getBoundingClientRect()
if (data.type === 'touchstart') {
const x = data.x - rect.left
const y = data.y - rect.top
const img = this.imgState
if (x >= img.x && x <= img.x + img.width && y >= img.y && y <= img.y + img.height) {
this.dragging = true
img.offsetX = x - img.x
img.offsetY = y - img.y
}
}
if (data.type === 'touchmove' && this.dragging) {
const x = data.x - rect.left
const y = data.y - rect.top
this.imgState.x = x - this.imgState.offsetX
this.imgState.y = y - this.imgState.offsetY
}
if (data.type === 'touchend') {
this.dragging = false
}
}
}
}
</script>
<script>
export default {
data() {
return {
canvasWidth: 0,
startStatus: false,
touchOperation: {
type: '',
x: 0,
y: 0
},
imageSrc: 'static/test/blurred_transparent_image.png'
}
},
onReady() {
this.$nextTick(() => {
uni.createSelectorQuery()
.select(".canvas")
.boundingClientRect(data => {
this.canvasWidth = data.width
this.startStatus = true
})
.exec()
})
},
methods: {
onTouchStart(e) {
const touch = e.touches[0]
this.touchOperation = {
type: 'touchstart',
x: touch.x,
y: touch.y
}
},
onTouchMove(e) {
const touch = e.touches[0]
this.touchOperation = {
type: 'touchmove',
x: touch.x,
y: touch.y
}
},
onTouchEnd() {
this.touchOperation = {
type: 'touchend'
}
},
// 新增:选择图片方法
chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ["original"],
success: (res) => {
// 获取临时文件路径
const tempFilePath = res.tempFilePaths[0]
this.imageSrc = tempFilePath;
console.log("图片路径: ", this.imageSrc);
// 通知 renderjs 模块更新图片路径
this.$nextTick(() => {
this.startStatus = false // 重置动画
setTimeout(() => {
this.startStatus = true // 重新触发 start
}, 50)
})
}
})
}
}
}
</script>
<style>
.page-body-wrapper {
text-align: center;
}
.canvas {
width: 610rpx;
height: 610rpx;
margin: auto;
background-color: #fff;
}
</style>

- 发布:2025-06-16 18:45
- 更新:2025-06-16 18:45
- 阅读:44
产品分类: uniapp/App
PC开发环境操作系统: Windows
PC开发环境操作系统版本号: 10.0.19045
HBuilderX类型: 正式
HBuilderX版本号: 4.66
手机系统: iOS
手机系统版本号: iOS 16
手机厂商: 苹果
手机机型: iPhone 8
页面类型: vue
vue版本: vue3
打包方式: 云端
项目创建方式: HBuilderX
示例代码:
操作步骤:
- 将图片附件保存到工程 static/test 目录下和手机相册
- 运行工程
- 点击 "选择图片" 按钮, 选择图片附件
- 将图片附件保存到工程 static/test 目录下和手机相册
- 运行工程
- 点击 "选择图片" 按钮, 选择图片附件
预期结果:
通过 chooseImage API 获取的图片有透明度
通过 chooseImage API 获取的图片有透明度
实际结果:
通过 chooseImage API 获取的图片没有透明度
通过 chooseImage API 获取的图片没有透明度
bug描述:
调用 chooseImage API 选择半透明图, chooseImage API 响应的图片没有透明度.
页面一开始就绘制 'static/test/blurred_transparent_image.png'(附件), 此时可以发现图片有透明度, 但是通过 chooseImage API 获取的附件没有透明度, 原因是图片的位深变成了24位.
