uni-app:iPhone的底部安全区域
发布时间:2022-09-13, 10:07:16 分类:HTML | 编辑 off 网址 | 辅助
正文 435字数 441,773阅读
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
Run code
Cut to clipboard
uni.getSysteminfo({
success: res => {
let safeArea = res.safeAreaInsets.bottom;
}
})
Run code
Cut to clipboard
该APi返回一个对象,包含 top right bottom left width height,其中bottom为安全区域高度
对应的兼容情况如下,uni-app的版本2.5.3+使用safeAreaInsets值
safeArea 在竖屏正方向下的安全区域 App、H5、微信小程序
safeAreaInsets 在竖屏正方向下的安全区域插入位置(2.5.3+) App、H5、微信小程序
(支付宝)给作者钱财以资鼓励 (微信)→
有过 3 条评论 »
"safearea": { "background":"#CCCCCC", // 安全区域外的背景颜色,默认值为"#FFFFFF" "bottom":{ // 底部安全区域配置 "offset":"none|auto" // 底部安全区域偏移,"none"表示不空出安全区域,"auto"自动计算空出安全区域,默认值为"none" }, "left": { // 左侧安全区域配置(横屏显示时有效) "offset":"none|auto" }, "right: { // 右侧安全区域配置(横屏显示时有效) "offset":"none|auto" } } iPhoneX的安全区域配置。
<template> <view> <canvas v-if="!canvas_src" style="width: 375px; height: 605px;margin-top:-1000px;position: absolute;top:-1000px;z-index: -1;" canvas-id="myCanvas" id="myCanvas"></canvas> <view class="tkxj_0"></view> <view class="tkxj_1" v-if="canvas_src"> <u-image :src="canvas_src" mode="heightFix" height="70vh" style="display:inline-block;"></u-image> <view class="ggbb_1" @click="close();">X</view> <view class="ggbb_2" @click="save_img();">保存图片</view> </view> </view> </template> <script> export default { name: 'add-img', props: { data_l:{ type:Object, default:()=>{} }, acv_index: { type: Number, default: 0 }, bgColor: { type: String, default: '#efefef' }, // 宽度,单位任意 width: { type: [String, Number], default: '100%' }, add_img_src: { type: Boolean, default: false }, // 是否懒加载,微信小程序、App、百度小程序、字节跳动小程序 lazyLoad: { type: Boolean, default: true }, // 背景颜色,用于深色页面加载图片时,为了和背景色融合 // close: { // type: Function, // default: ()=>{} // }, bgColor: { type: String, default: '#f3f4f6' }, getConfig:{ type:Object, default:()=>{} }, goods_info:{ type:Object, default:()=>{} } }, data() { return { backgroundStyle: {}, // getConfig: {}, num:1, canvas_src:'', qrcode:'' // v_show_d:this.v_show }; }, watch: { }, computed: { }, onShow() { // this.$nextTick(() => { // console.log(2132131) // this.nGetConfig(this) // }) }, mounted: function () { let res2 = wx.getSystemInfoSync(); this.xw_whith = 375;//res2.windowWidth; this.xw_height = 603;//res2.windowHeight; // console.log(res2); // console.log(2132131) // this.nGetConfig(this,(res)=>{ // //getConfig 子子孙传值不过来 临时解决办法 // // console.log(res) // this.getConfig=res; // this.drawShareImg(); // }) // console.log(this.acv_index) this._shop_details_qrcode(); // this.drawShareImg(); }, methods: { //生成二维码 _shop_details_qrcode(){ this.nAjax({ url: "index/_shop_details_qrcode", // clogin: true, showLoading: true, // showLoadingTitle: "正在发送", // 'header':{ // 'content-type':'application/x-www-form-urlencoded', // 'test':123 // }, // 'method':'GET', data: { tuid: this.getConfig._fx.txt.uid, tshopid: this.goods_info.id, }, successShow: { show: false, type: "toast", // 'icon':'success', duration: 1500, // type: "modal", // title: "提示", // showCancel: false, // // 'cancelText':'取消', // // 'confirmText':'确定', // confirm: () => { // console.log("用户点击确定"); // }, // 'cancel':()=>{ // console.log('用户点击取消'); // }, }, errorShow: { show: true, // 'type':'toast', // 'icon':'error', // 'duration':3500, type: "modal", title: "", showCancel: false, // 'cancelText':'取消', confirmText: "知道了", confirm: () => { // console.log("用户点击确定"); }, // 'cancel':()=>{ // console.log('用户点击取消'); // }, }, success: (res) => { this.qrcode=res.data.qrcode; this.drawShareImg(); }, // fail:()=>{ // }, // complete:()=>{ // } }); }, _getImageInfo(src,callback){ uni.getImageInfo({ 'src':src, 'success':res=>{ callback(res) } }); }, // 合成分享图核心代码 drawShareImg() { uni.showLoading({ title: '加载中' }); let img_arr=[ { 'src':this.goods_info.cover_img, //商品封面 '_x':this.getConfig._fx.position.dt._x, '_y':this.getConfig._fx.position.dt._y, }, { 'src':this.getConfig['all']['list2']['15'], //底图 '_x':this.getConfig._fx.dt._x, '_y':this.getConfig._fx.dt._y, }, { 'src':this.getConfig._fx.txt.avatarUrl || this.getConfig['all']['list2']['16'], //头像 '_x':this.getConfig._fx.position.tx._x, '_y':this.getConfig._fx.position.tx._y, }, { 'src':this.qrcode, //二维码 'http://jgy.com/'+ '_x':this.getConfig._fx.position.ewm._x, '_y':this.getConfig._fx.position.ewm._y, } // this.getConfig._fx.ewm ]; // console.log(img_arr) let img_arr_new=[]; this._getImageInfo(img_arr[0].src,res=>{ //封面 // let width=this.xw_whith; // let height=res.height*width/res.width; let height=this.xw_height-160; let width=res.width*height/res.height; if(width>this.xw_whith){ img_arr[0]._x=0-(width-this.xw_whith)/2; } res.width=width res.height=height res._x=img_arr[0]._x; res._y=img_arr[0]._y; img_arr_new.push(res); this._getImageInfo(img_arr[1].src,res=>{ //底图 let width=this.xw_whith; let height=this.xw_height; //this.xw_whith*res.width/this.xw_height res.width=width res.height=height img_arr_new.push(res) this._getImageInfo(img_arr[2].src,res=>{ //头像 res._x=img_arr[2]._x; res.height=res.height*60/res.width; res.width=60; res._y=this.xw_height-img_arr[2]._y-res.height; img_arr_new.push(res) this._getImageInfo(img_arr[3].src,res=>{ //二维码 // let width=this.xw_whith; // let height=res.height*width/res.width; let height=100; let width=100; res.width=width res.height=height res._x=this.xw_whith-img_arr[3]._x-res.width; res._y=this.xw_height-img_arr[3]._y-res.height; img_arr_new.push(res) this.drawShareImg0(img_arr_new); }) }) }) }) }, /* * 参数说明 * ctx Canvas实例 * img 图片地址 * x x轴坐标 * y y轴坐标 * w 宽度 * h 高度 * r 弧度大小 */ circleImg(ctx, img, x, y, r=13, w=50, h=50) { ctx.save(); // 画一个图形 if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); ctx.strokeStyle = '#FFFFFF'; // 设置绘制圆形边框的颜色 ctx.stroke(); ctx.clip(); ctx.drawImage(img, x, y, w, h); ctx.restore(); }, /* * 圆角图片=>参数说明 * ctx Canvas实例 * img 图片地址 * x x轴坐标 * y y轴坐标 * r 圆形半径 */ circleImg2(ctx, img, x, y, r) { ctx.save(); let d = 2 * r; let cx = x + r; let cy = y + r; ctx.arc(cx, cy, r, 0, 2 * Math.PI); ctx.clip(); ctx.drawImage(img, x, y, d, d); ctx.restore(); }, drawShareImg0(img_arr_new) { // let that=this; //用户名 let uname=this.getConfig._fx.txt.uname; let txt1=this.getConfig._fx.txt.txt1; let txt2=this.getConfig._fx.txt.txt2; let shop_title=this.goods_info.title; let price='¥'+this.goods_info.price; let price2='¥'+this.goods_info.market_price; if(!this.goods_info.market_price || this.goods_info.market_price<0.01) price2=''; let ww, hh; const ctx = uni.createCanvasContext('myCanvas',this) ww = this.xw_whith; //准确的宽高 hh = this.xw_height; // ctx.drawImage("https://img.jinguiyuan.cn/FkZdT5PqHitcbcVzGhstB8jasKXQ", 0, 0, ww, hh) // ctx.drawImage("https://img.jinguiyuan.cn/FkZdT5PqHitcbcVzGhstB8jasKXQ", 30, hh - 87, 50, 50) //封面 ctx.drawImage(img_arr_new[0].path, img_arr_new[0]._x, img_arr_new[0]._y,img_arr_new[0].width,img_arr_new[0].height); //底图 ctx.drawImage(img_arr_new[1].path, img_arr_new[1]._x, img_arr_new[1]._y,img_arr_new[1].width,img_arr_new[1].height); //头像 this.circleImg(ctx,img_arr_new[2].path, img_arr_new[2]._x, img_arr_new[2]._y); //二维码 ctx.drawImage(img_arr_new[3].path, img_arr_new[3]._x, img_arr_new[3]._y,img_arr_new[3].width,img_arr_new[3].height); // img_arr_new.forEach((v,i)=>{ // if(2==i){ // this.circleImg(ctx,v.path, v._x, v._y,30) // }else ctx.drawImage(v.path, v._x, v._y,v.width,v.height); // }) ctx.setFontSize(16) ctx.setFillStyle('#000') ctx.fillText(uname, 100, hh - 105) ctx.setFontSize(14) ctx.setFillStyle('#999') let u_w=120+ctx.measureText(uname).width; //100+uname.length*17+8 ctx.fillText(txt1, u_w, hh - 105) //商品标题 // console.log(333) // console.log(ctx.measureText(shop_title.substring(0,15)).width) let si=0; let s_shop_title=shop_title; for(let i=0;i<=shop_title.length;i++){ // console.log(ctx.measureText(shop_title.substring(0,i)).width) let ss_t=shop_title.substring(0,i)+'...'; if(!si && ctx.measureText(ss_t).width>178) { si=i; s_shop_title=ss_t; } } ctx.setFontSize(18) ctx.setFillStyle('#000') ctx.fillText(s_shop_title, 18, hh - 55) //会员价 文字 ctx.setFontSize(14) ctx.setFillStyle('#000') ctx.fillText(txt2, 20, hh - 25) //会员价 ctx.setFontSize(18) ctx.setFillStyle('#b12704') ctx.fillText(price, 65, hh - 25) //市场价 if(price2){ ctx.setFontSize(16) ctx.setFillStyle('#9fa1a0') let p_w=82+ctx.measureText(price).width; ctx.fillText(price2, p_w, hh - 25) //删除线 ctx.beginPath(); const textWidth = ctx.measureText(price2).width; ctx.rect(p_w, hh - 31, textWidth+3, 1); ctx.fillStyle = '#9fa1a0'; ctx.fill(); } //开始绘画 ctx.draw() //这里需要做个延迟防止绘制没结束生成图片黑的问题 setTimeout(() => { uni.canvasToTempFilePath({ canvasId: 'myCanvas', success: (res)=> { // 在H5平台下,tempFilePath 为 base64 // console.log(res.tempFilePath) this.canvas_src=res.tempFilePath; uni.hideLoading(); } },this) }, 1000); }, close(){ // console.log(this.v_show); this.$emit("close"); // this.blurInput(,); }, save_img(){ uni.authorize({ scope: 'scope.writePhotosAlbum', success:()=> { //保存图片 uni.saveImageToPhotosAlbum({ filePath:this.canvas_src, success:(res)=> { this.close(); uni.showToast({ title: '保存成功', duration: 2000 }); uni.vibrateLong(); } }) }, fail:()=>{ uni.showModal({ title: '提示', confirmText:'去设置', content: '请授权相册', success: (res)=> { if (res.confirm) { uni.openSetting(); // console.log('用户点击确定'); } else if (res.cancel) { // console.log('用户点击取消'); } } }); return; //只有用户主动操作下的直接回调才会生效。非用户操作或间接回调都不会拉起setting页面 uni.showToast({ title: '请授权相册', duration: 2000 }); setTimeout(()=>{ uni.openSetting(); },1500) // console.log(12312) } }) } }, }; </script> <style scoped> .tkxj_0{ position: fixed; left: 0px; top: 0px; width: 100vw; height: 100vh; z-index: 99998; background: #000; opacity: 0.7; } .tkxj_1{ position: fixed; top: 140rpx; left: 0px; width: 100vw; z-index: 99999; padding-bottom: 100px; text-align: center; } .ggbb_1{ position: absolute; color: #fff; left: 36rpx; top: -45px; border: 1px solid #fff; font-size: 16px; width: 30px; height: 30px; line-height: 28px; border-radius: 50%; } .ggbb_2{ color: #fff; font-size: 18px; border: 1px solid #fff; height: 36px; line-height: 36px; border-radius: 12px; display: inline-block; padding: 0px 15px; position: absolute; bottom: 30rpx; left: 50%; margin-left: -52px; width: 104px; } </style>
制作圆形和圆角矩形并不是一个方法,但大同小异
圆形使用的是:arc()
圆角使用的是:arcTo()
圆形:利用 Canvas 先画出一个圆形,然后将图片定位到圆形中心位置进行剪切,将超出圆形的部分去掉,就会形成一个圆形
圆角:利用 Canvas 先画出一个圆角矩形,然后将图片定位到圆角矩形位置进行剪切,将超出圆形的部分去掉,就会形成一个圆角矩形
区别在于,圆角需要我们一段一段的自己画出来,而圆形有现成的方法只用设置想要的值即可
// 开始制作头像 createPlacard() { uni.getImageInfo({ src: '...7/02/e0eb38388da1c.jpg', // 网络图片需先下载,得到临时本地路径,否则绘入 Canvas 可能会出现空白 success: (img)=> { const ctx = wx.createCanvasContext('myCanvas', this); ctx.fillStyle = "#FFFFFF"; ctx.fillRect(0, 0, uni.upx2px(750), uni.upx2px(1000)); // 如何在 Canvas 中绘入圆形图片? // 原理:利用 Canvas 先画出一个圆形,然后将图片定位到圆形中心位置进行剪切,将超出圆形的部分去掉,就会形成一个圆形 this.circleImgOne(ctx, img.path, uni.upx2px(175), uni.upx2px(95), uni.upx2px(200)); // 如何在 Canvas 中绘入圆角矩形? // 原理:利用 Canvas 先画出一个圆角矩形,然后将图片定位到圆角矩形位置进行剪切,将超出圆形的部分去掉,就会形成一个圆角矩形 //this.circleImgTwo(ctx, img.path, uni.upx2px(105), uni.upx2px(95), uni.upx2px(512), uni.upx2px(382), uni.upx2px(20)); ctx.draw(); uni.hideLoading() } }) }
/* * 参数说明 * ctx Canvas实例 * img 图片地址 * x x轴坐标 * y y轴坐标 * r 圆形半径 */ circleImgOne(ctx, img, x, y, r) { // 如果在绘制图片之后还有需要绘制别的元素,需启动 save() 、restore() 方法,否则 clip() 方法会导致之后元素都不可见 // save():保存当前 Canvas 画布状态 // restore():恢复到保存时的状态 /* ctx.save(); */ let d = r * 2; let cx = x + r; let cy = y + r; ctx.arc(cx, cy, r, 0, 2 * Math.PI); ctx.strokeStyle = '#FFFFFF'; // 设置绘制圆形边框的颜色 ctx.stroke(); // 绘制出圆形,默认为黑色,可通过 ctx.strokeStyle = '#FFFFFF', 设置想要的颜色 ctx.clip(); ctx.drawImage(img, x, y, d, d); /* ctx.restore(); */ },
/* * 参数说明 * ctx Canvas实例 * img 图片地址 * x x轴坐标 * y y轴坐标 * w 宽度 * h 高度 * r 弧度大小 */ circleImgTwo(ctx, img, x, y, w, h, r) { // 画一个图形 if (w < 2 * r) r = w / 2; if (h < 2 * r) r = h / 2; ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r); ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath(); ctx.strokeStyle = '#FFFFFF'; // 设置绘制圆形边框的颜色 ctx.stroke(); ctx.clip(); ctx.drawImage(img, x, y, w, h); }
drawText(ctx, txtwid, t, x, y, w){ var chr = t.split(""); var temp = ""; var row = []; ctx.font = "20px Arial"; ctx.fillStyle = "black"; ctx.textBaseline = "middle"; for(var a = 0; a < chr.length; a++){ if( ctx.measureText(temp).width < w ){ ; } else{ row.push(temp); temp = ""; } temp += chr[a]; } row.push(temp); for(var b = 0; b < row.length; b++){ ctx.setFontSize(txtwid); ctx.setFillStyle("#333333"); ctx.fillText(row[b],x,y+(b+1)*20); } },
canvas组件注册无效,这个因为createCanvasContext方法是有两个参数,
在page页面默认传了一个this,在组件里面需要手动传this。
const ctx = wx.createCanvasContext('myCanvas',this);
在vue2中使用 ::v-deep
::v-deep .el-col { margin-bottom: 20px; }
’ >>> ‘和’ /deep/ '支持已弃用。
在vue3中::v-deep可以使用但是不推荐使用,官方推荐v-deep(.className)
::v-deep(.el-col) { margin-bottom: 20px; } // 缩写 :deep(.el-col) { margin-bottom: 20px; }
全局样式设置 ::v-global(.ClassName) 缩写 :global(.ClassName) 插槽内的样式设置 ::v-slotted(.ClassName) 缩写 :slotted(.ClassName)