收藏
回答

canvas安卓Android真机不显示,IOS苹果手机正常显示怎么处理?

如上图左IOS,右安卓,canvas生成海报,ISO下是显示,测试机iphone13,ios版本16.2;

安卓手机是红米9A,安卓版本11,安卓手机另一部是华为nova9,安卓版本12.0.1,两部安卓都不显示,

开发者工具是正常显示的,版本库是2.30.0。

代码如下:

<!-- 主页面 -->
<template>
	<view class="content">
		<!-- 分享弹窗-->
		<hch-poster v-if="show" ref="hchPoster" @cancel="handleCancel" :posterData.sync="posterData"
			@previewImage="previewImage" />
	</view>
</template>
<script>
	import {
		getwxacodeunlimit
	} from '../../api/common.js';
	import base64src from '../../components/base64Topath.js' //引入base64Topath.js
	export default {
		data() {
			return {
				show: false,
				// 海报模板数据
				posterData: {
					poster: {
						// http://yny-file.oss-cn-shanghai.aliyuncs.com/upload/20221205/a641ce554786c034dd8679e3906cd0b8.png
						//根据屏幕大小自动生成海报背景大小
						// url: 'http://yny-file.oss-cn-shanghai.aliyuncs.com/upload/20221205/9f9d77aa47a406ec016f52bd8c63ee1b.png', //图片地址
						url: 'https://file.qimaiziyuanwang.com/qmt/2022/12/15/5927a96ba0734f4c8ab2e8df4c95d099.png', // 求购
						r: 10, //圆角半径
						w: 312, //海报宽度
						h: 496, //海报高度
						p: 16 //海报内边距padding
					},
					title: {
						//商品标题
						text: '橘猫卡(一卡多网)', //文本
						fontSize: 16, //字体大小
						color: '#141414', //颜色
						lineHeight: 22, //行高
						mt: 43 //margin-top
					},
					content: {
						//商品标题
						text: '三大电信运营商认可的一张一卡多网卡品即将隆重上线。卡品包括三大电三大电商三大电信运营商认可的一张一卡多网卡品即将隆重...', //文本
						fontSize: 14, //字体大小
						color: '#606266', //颜色
						lineHeight: 24, //行高
						mt: 79 //margin-top
					},
					mainImg: {
						//海报主商品图
						url: 'https://huangchunhongzz.gitee.io/imgs/poster/product.png', //图片地址
						r: 10, //圆角半径
						w: 280, //宽度
						h: 158, //高度
						mt: 185 //margin-top
					},
					codeImg: {
						//小程序码
						url: 'https://huangchunhongzz.gitee.io/imgs/poster/code.png', //图片地址
						w: 88, //宽度
						h: 88, //高度
						mt: 50, //margin-top
						r: 50, //圆角半径
						align: 'right', //对齐方式
					},
					tips: [
						//提示信息
						{
							text: '区域:上海徐汇', //文本
							fontSize: 14, //字体大小
							color: '#333333', //字体颜色
							align: 'left', //对齐方式
							lineHeight: 25, //行高
							mt: 30 //margin-top
						}, {
							text: '时间:2022-09-27', //文本
							fontSize: 12, //字体大小
							color: '#9196A1', //字体颜色
							align: 'left', //对齐方式
							lineHeight: 25, //行高
							mt: 30 //margin-top
						}
					]
				}
			}
		},
		onLoad(option) {
			if (option.type === '供应') {
				this.posterData.poster.url =
					'https://file.qimaiziyuanwang.com/qmt/2022/12/15/8d20104c83744f3890621434ecfa6727.png';
			}
			if (option.type === '求购') {
				this.posterData.poster.url =
					'https://file.qimaiziyuanwang.com/qmt/2022/12/15/5927a96ba0734f4c8ab2e8df4c95d099.png';
			}
			this.posterData.mainImg.url = option.imgurl;
			this.posterData.title.text = option.businessTitle;
			this.posterData.content.text = option.businessContent;
			this.posterData.tips[0].text = option.citytext;
			this.posterData.tips[1].text = option.createTime;
			this.posterData = JSON.parse(JSON.stringify(this.posterData));
			this.fetchgetwxacodeunlimit(option.id);
		},
		methods: {
			fetchgetwxacodeunlimit(id) { // 获取太阳码
				getwxacodeunlimit({
					"page": "pages/supplydetails/index",
					"scene": id
				}).then(response => {
					if (response.code === 200) {
						base64src(response.data, resCurrent => {
							this.posterData.codeImg.url = resCurrent;
							console.log(this.posterData.codeImg.url)
							this.show = true;
						})
					}
				})
			},
			// 取消海报
			handleCancel(val) {},
			// h5的情况下,点击海报保存按钮到图片预览海报,可以长按保存
			previewImage(base64) {
				// 预览图片
				uni.previewImage({
					urls: [base64]
				})
			}
		}
	}
</script>
<style lang="scss">
</style>



<!-- ==========================以下是组件hch-poster部分========================================================== -->
<template>
	<view class="canvas-content" v-show="canvasShow" :style="'width:' + system.w + 'px; height:' + system.h + 'px;'">
		<!-- #ifndef MP-TOUTIAO || MP-BAIDU -->
		<view class="canvas-tit" @tap="goBack()">
			<image src="../../static/image/img/leftimg.png" mode=""></image>
			<view class="title">企脉圈</view>
		</view>
		<!-- #endif -->
		<!-- 背景层 -->
		<image class="canvasImg" src="../../static/image/canvasBg.png" mode="widthFix"></image>
		<!-- 海报 -->
		<!-- :width="system.w" :height="system.h" 支付宝必须要这样设置宽高才有效果 -->
		<canvas class="canvas" canvas-id="myCanvas" id="myCanvas"
			:style="'width:' + system.w + 'px; height:' + system.h + 'px;'" :width="system.w"
			:height="system.h"></canvas>
		<view class="button-wrapper">
			<!-- 保存海报按钮 -->
			<!-- #ifndef MP-QQ -->
			<!-- cover-view 标签qq小程序有问题 -->
			<cover-view class="save-btn" @tap="handleSaveCanvasImage">保存图片到相册</cover-view>
			<!-- #endif -->
			<!-- #ifdef MP-QQ -->
			<view class="save-btn" @tap="handleSaveCanvasImage">保存图片到相册</view>
			<!-- #endif -->
		</view>
	</view>
</template>
<script>
	import {
		drawSquarePic,
		drawTextReturnH,
		getSystem
	} from './utils'
	export default {
		data() {
			return {
				system: {},
				canvasShow: false
			}
		},
		props: {
			posterData: {
				type: Object,
				default: () => {
					return {}
				}
			}
		},
		computed: {
			// 计算海报背景数据
			poster() {
				let data = this.posterData
				let system = this.system
				let posterBg = {
					url: data.poster.url,
					r: data.poster.r * system.scale,
					w: data.poster.w * system.scale,
					h: data.poster.h * system.scale,
					x: (system.w - data.poster.w * system.scale) / 2,
					y: (system.h - data.poster.h * system.scale) / 2,
					p: data.poster.p * system.scale
				}
				return posterBg
			},
			// 计算海报标题
			title() {
				let data = this.posterData
				let system = this.system
				let posterTitle = data.title
				posterTitle.x = this.poster.x
				posterTitle.y = this.poster.y + data.poster.p * system.scale + data.title.mt * system.scale
				return posterTitle
			},
			// 计算海报内容
			content() {
				let data = this.posterData
				let system = this.system
				let posterContent = data.content
				posterContent.x = this.poster.x
				posterContent.y = this.poster.y + data.poster.p * system.scale + data.content.mt * system.scale
				return posterContent
			},
			// 计算海报头部主图
			mainImg() {
				let data = this.posterData
				let system = this.system
				let posterMain = {
					url: data.mainImg.url,
					r: data.mainImg.r * system.scale,
					w: data.mainImg.w * system.scale,
					h: data.mainImg.h * system.scale,
					x: (system.w - data.mainImg.w * system.scale) / 2,
					y: this.poster.y + data.poster.p * system.scale + data.mainImg.mt * system.scale
				}
				return posterMain
			},
			// 计算小程序码
			codeImg() {
				let data = this.posterData
				let system = this.system
				let posterCode = {
					url: data.codeImg.url,
					r: data.codeImg.r * system.scale,
					w: data.codeImg.w * system.scale,
					h: data.codeImg.h * system.scale,
					x: (system.w - ((data.poster.w * system.scale) / 2)) + data.poster.p * system.scale,
					y: this.mainImg.y + data.poster.p * system.scale + data.mainImg.mt * system.scale
				}
				return posterCode
			},
		},
		created() {
			// 获取设备信息
			this.system = getSystem()
		},
		mounted() {
			var t = setTimeout(() => {
				this.creatPoster()
				clearTimeout(t);
			}, 2000)
		},
		methods: {
			goBack() {
				uni.navigateBack({
					delta: 1
				});
			},
			// 展示海报
			// posterShow() {
			//   this.canvasShow = true
			//   this.creatPoster()
			// },
			// 生成海报
			async creatPoster() {
				uni.showLoading({
					title: '生成海报中...'
				})
				const ctx = uni.createCanvasContext('myCanvas', this)
				this.ctx = ctx
				ctx.clearRect(0, 0, this.system.w, this.system.h) //清空之前的海报
				ctx.draw() //清空之前的海报
				// 根据设备屏幕大小和距离屏幕上下左右距离,及圆角绘制背景
				let poster = this.poster
				let title = this.title
				let content = this.content
				let mainImg = this.mainImg
				let codeImg = this.codeImg
				await drawSquarePic(ctx, poster.x, poster.y, poster.w, poster.h, poster.r, poster.url)
				await drawSquarePic(ctx, mainImg.x, mainImg.y, mainImg.w, mainImg.h, mainImg.r, mainImg.url)
				// 绘制标题 textY 绘制文本的y位置
				console.log('creatPoster -> title.x', title.x)
				let textY = drawTextReturnH(ctx, title.text, title.x, title.y, mainImg.w, title.fontSize, title.color,
					title.lineHeight, )
				// 绘制内容contentY绘制文本的y位置
				let contentY = drawTextReturnH(ctx, content.text, content.x, content.y, mainImg.w, content.fontSize,
					content.color, content.lineHeight)
				// 绘制小程序码
				await drawSquarePic(ctx, codeImg.x, codeImg.y, codeImg.w, codeImg.h, codeImg.r, codeImg.url, codeImg
					.mt, )
				// 小程序的名称
				// 长按/扫描识别查看商品
				let y = 0
				this.posterData.tips.forEach((element, i) => {
					if (i == 0) {
						y = codeImg.y + element.mt * this.system.scale
					} else {
						y += element.mt
					}
					y = drawTextReturnH(ctx, element.text, title.x, y, mainImg.w, element.fontSize, element
						.color, element.lineHeight, element.align)
				})
				this.canvasShow = true
				uni.hideLoading()
			},
			// 保存到系统相册
			handleSaveCanvasImage() {
				uni.showLoading({
					title: '保存中...'
				})
				let _this = this
				// 把画布转化成临时文件
				// #ifndef MP-ALIPAY
				// 支付宝小程序外,其他都是用这个方法 canvasToTempFilePath
				uni.canvasToTempFilePath({
					x: this.poster.x,
					y: this.poster.y,
					width: this.poster.w, // 画布的宽
					height: this.poster.h, // 画布的高
					destWidth: this.poster.w * 5,
					destHeight: this.poster.h * 5,
					canvasId: 'myCanvas',
					success(res) {
						//保存图片至相册
						// #ifndef H5
						// 除了h5以外的其他端
						uni.saveImageToPhotosAlbum({
							filePath: res.tempFilePath,
							success(res) {
								uni.hideLoading()
								uni.showToast({
									title: '图片保存成功,可以去分享啦~',
									duration: 2000,
									icon: 'none'
								})
							},
							fail() {
								uni.showToast({
									title: '保存失败,稍后再试',
									duration: 2000,
									icon: 'none'
								})
								uni.hideLoading()
							}
						})
						// #endif
						// #ifdef H5
						// h5的时候
						uni.showToast({
							title: '请长按保存',
							duration: 3000,
							icon: 'none'
						})
						_this.$emit('previewImage', res.tempFilePath)
						// #endif
					},
					fail(res) {
						console.log('fail -> res', res)
						uni.showToast({
							title: '保存失败,稍后再试',
							duration: 2000,
							icon: 'none'
						})
						uni.hideLoading()
					}
				}, this)
				// #endif
				// #ifdef MP-ALIPAY
				// 支付宝小程序条件下 toTempFilePath
				this.ctx.toTempFilePath({
					x: this.poster.x,
					y: this.poster.y,
					width: this.poster.w, // 画布的宽
					height: this.poster.h, // 画布的高
					destWidth: this.poster.w * 5,
					destHeight: this.poster.h * 5,
					success(res) {
						//保存图片至相册
						my.saveImage({
							url: res.apFilePath,
							showActionSheet: true,
							success(res) {
								uni.hideLoading()
								uni.showToast({
									title: '图片保存成功,可以去分享啦~',
									duration: 2000,
									icon: 'none'
								})
							},
							fail() {
								uni.showToast({
									title: '保存失败,稍后再试',
									duration: 2000,
									icon: 'none'
								})
								uni.hideLoading()
							}
						})
					},
					fail(res) {
						console.log('fail -> res', res)
						uni.showToast({
							title: '保存失败,稍后再试',
							duration: 2000,
							icon: 'none'
						})
						uni.hideLoading()
					}
				}, this)
				// #endif
			},
		}
	}
</script>
<style lang="scss">
	.canvas-content {
		height: 100vh;
		overflow: hidden;
		position: relative;


		.canvasImg {
			height: 100%;
			width: 100%;
			position: absolute;
			z-index: -1;
			left: 0;
			top: 0
		}


		.canvas-tit {
			position: absolute;
			font-size: 32rpx;
			font-weight: 400;
			height: 88rpx;
			color: #FFFFFF;
			display: flex;
			align-items: center;
			top: 70rpx;
			left: 40rpx;
			z-index: 10;


			image {
				width: 20rpx;
				height: 20rpx;
			}


			.title {
				margin-left: 268rpx;
			}
		}


		.canvas {}


		.button-wrapper {
			width: 624rpx;
			margin: 0 63rpx;
			position: fixed;
			bottom: 20rpx;
		}


		.save-btn {
			height: 88rpx;
			line-height: 88rpx;
			text-align: center;
			background: linear-gradient(180deg, #FFEFB4 0%, #F9C270 100%);
			box-shadow: 0rpx 3rpx 6rpx rgba(0, 0, 0, 0.16);
			opacity: 1;
			color: #472B00;
			font-weight: bold;
			font-size: 28rpx;
			border-radius: 44rpx;
		}
	}
</style>

<!-- ==========================以下是工具utils文件========================================================== -->
/*
 * @Description: 公共方法
 * @Version: 1.0.0
 * @Autor: hch
 * @Date: 2021-07-22 00:01:09
 */
/**
 * @description: 绘制正方形(可以定义圆角),并且有图片地址的话填充图片
 * @param {CanvasContext} ctx canvas上下文
 * @param {number} x 圆角矩形选区的左上角 x坐标
 * @param {number} y 圆角矩形选区的左上角 y坐标
 * @param {number} w 圆角矩形选区的宽度
 * @param {number} h 圆角矩形选区的高度
 * @param {number} r 圆角的半径
 * @param {String} url 图片的url地址
 */
export function drawSquarePic(ctx, x, y, w, h, r, url) {
  ctx.save()
  ctx.beginPath()
  // 绘制左上角圆弧
  ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
  // 绘制border-top
  // 画一条线 x终点、y终点
  ctx.lineTo(x + w - r, y)
  // 绘制右上角圆弧
  ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
  // 绘制border-right
  ctx.lineTo(x + w, y + h - r)
  // 绘制右下角圆弧
  ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)
  // 绘制左下角圆弧
  ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)
  // 绘制border-left
  ctx.lineTo(x, y + r)
  // 填充颜色(需要可以自行修改)
  // #ifndef MP-TOUTIAO
  ctx.setFillStyle('transparent')
  // #endif
  // #ifdef MP-TOUTIAO
  ctx.setFillStyle('#5E92EF')
  // #endif
  ctx.fill()
  // 剪切,剪切之后的绘画绘制剪切区域内进行,需要save与restore 这个很重要 不然没办法保存
  ctx.clip()


  // 绘制图片
  return new Promise((resolve, reject) => {
    if (url) {
      wx.getImageInfo({
        src: url,
        success(res) {
          ctx.drawImage(res.path, x, y, w, h)
          ctx.restore() //恢复之前被切割的canvas,否则切割之外的就没办法用
          ctx.draw(true)
          resolve()
        },
        fail(res) {
          console.log('fail -> res', res)
          uni.showToast({
            title: '图片下载异常',
            duration: 2000,
            icon: 'none'
          })
        }
      })
    } else {
      ctx.draw(true)
      resolve()
    }
  })
}


/**
 * @description: 获取设备信息
 * @param {type}
 * @return {type}
 * @author: hch
 */
export function getSystem() {
  let system = wx.getSystemInfoSync()
  let scale = system.windowWidth / 375 //按照苹果留 375*667比例 其他型号手机等比例缩放 显示
  return { w: system.windowWidth, h: system.windowHeight, scale: scale }
}


/**
 * @description: 绘制文本时文本的总体高度
 * @param {Object} ctx canvas上下文
 * @param {String} text 需要输入的文本
 * @param {Number} x X轴起始位置
 * @param {Number} y Y轴起始位置
 * @param {Number} maxWidth 单行最大宽度
 * @param {Number} fontSize 字体大小
 * @param {String} color 字体颜色
 * @param {Number} lineHeight 行高
 * @param {String} textAlign 字体对齐方式
 */
export function drawTextReturnH(
  ctx,
  text,
  x,
  y,
  maxWidth = 375,
  fontSize = 14,
  color = '#000',
  lineHeight = 30,
  textAlign = 'left'
) {
  if (textAlign) {
    ctx.setTextAlign(textAlign) //设置文本的水平对齐方式  ctx.setTextAlign这个可以兼容百度小程序 ,注意:ctx.textAlign百度小程序有问题
    switch (textAlign) {
      case 'center':
        x = getSystem().w / 2
        break


      case 'right':
        x = (getSystem().w - maxWidth) / 2 + maxWidth
        break


      default:
        // 左对齐
        x = (getSystem().w - maxWidth) / 2
        break
    }
  }
  let arrText = text.split('')
  let line = ''
  for (let n = 0; n < arrText.length; n++) {
    let testLine = line + arrText[n]
    ctx.font = fontSize + 'px sans-serif' //设置字体大小,注意:百度小程序 用ctx.setFontSize设置字体大小后,计算字体宽度会无效
    ctx.setFillStyle(color) //设置字体颜色
    let metrics = ctx.measureText(testLine) //measureText() 方法返回包含一个对象,该对象包含以像素计的指定字体宽度。
    let testWidth = metrics.width
    if (testWidth > maxWidth && n > 0) {
      ctx.fillText(line, x, y)
      line = arrText[n]
      y += lineHeight
    } else {
      line = testLine
    }
  }
  ctx.fillText(line, x, y)
  ctx.draw(true) //本次绘制是否接着上一次绘制。即 reserve 参数为 false,则在本次调用绘制之前 native 层会先清空画布再继续绘制;若 reserve 参数为 true,则保留当前画布上的内容,本次调用 drawCanvas 绘制的内容覆盖在上面,默认 false。
  return y
}


回答关注问题邀请回答
收藏

1 个回答

登录 后发表内容