小程序图片自适应组件 仿微信聊天图片

(转载请注明出处,本文章内容仅用于学习)


这几天开发小程序有个新需求,需要在聊天群中实现发送图片。
之前做聊天时已经把框架搭好,心想应该只需要后端增加上传图片的一系操作。
当然最后做出来是没问题,但是不论用哪个小程序的图片自动缩放/截取,显示效果都差强人意,最后综合考虑(百度了一大圈都没有),只能手写图片自适应算法

最终想要实现的效果

目前这种聊天群的图片显示,用户最习惯的还是微信跟QQ的显示模式,参考微信的图片最终样式大体上划分为缩放与截取。
一种是不管图片原来有多大,发送后都会缩放到正常比例显示给用户,另一种通常是图片的宽高比例相差极大,如果缩放到用户的能一眼看完的程度,通常很不美观,所以一般会截取多余长/宽的那一部分,只显示一部分,用户如果想看完整图片需点击查看。
最后要能对图片的样式进行控制跟原来的一样。


解决方案

简略:使用js获取图片宽高并使用算法得出合理的显示宽高,最后将宽高动态赋值给wxml的图片,截取则利用图片定义的视窗区域。
一张图片在聊天室发出后,怎么知道这张图片要不要缩放/裁剪?这里做法是相对于屏幕,定义一块图片最大能显示多少的视窗区域,如果图片宽高任意一边超出这个视窗区,就要进行缩放操作,至于怎么缩放以及缩放多少合适请看代码。


考虑使用场景在数据量很多的消息列表中,最终选择将整体代码样式封装进组件中使用,实测使用组件加载图片跟不使用加载基本无差别。

组件代码
这里是针对聊天场景调试优化的版本,下面有通用版,不会创建组件的搜别人贴子,这里不做演示直接贴代码

JS

Component({
  lifetimes:{
    detached: function() {
      //console.log("组件实例被从页面节点树移除");
    },
    attached: function(){
      //console.log("组件实例进入页面节点树");
      
      var that = this;
      wx.getImageInfo({ 
        src: that.properties.imgUrl,
        success: function (res) {  
          var imgWidth=res.width;
          var imgHeight=res.height;

          var maxPictureViewWidth = 280;//图片视窗最大宽度
          var maxPictureViewHeight = 220;//图片视窗最大高度

          var ratio = imgWidth/imgHeight;

          if(ratio<0.25 || ratio>2.2){//图片宽高比相差过大,可能需要截取
            //判断图片是过高还是过宽
            if(imgWidth>imgHeight){//图片过宽
              if(imgHeight>maxPictureViewHeight){//同时判断图片高度有没有大于视窗最大高度
                //图片过宽且图片高度大于视窗最大高度,依据高度缩减图片比例到视窗可见范围,宽度不管直接靠视窗截取多的部分不显示
                var product = maxPictureViewHeight/imgHeight;
                imgHeight=imgHeight*product
                imgWidth=imgWidth*product
                that.setData({
                  viewHeight:maxPictureViewHeight<imgHeight/2?maxPictureViewHeight:imgHeight/2,
                  viewWidth:maxPictureViewWidth<imgWidth/2?maxPictureViewWidth:imgWidth/2,
                  imgHeight:imgHeight/2,
                  imgWidth:imgWidth/2
                })
              }else{//图片高度小于视窗最大高度
                that.setData({
                  viewHeight:imgHeight/2,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight/2,
                  imgWidth:imgWidth/2
                })
              }
            }else{//图片过高
              if(imgWidth>maxPictureViewWidth){
                //图片过高且图片宽度大于视窗最大宽度,按照宽度缩减图片比例到视窗可见范围,高度不管直接靠视窗截取不显示
                var product = maxPictureViewWidth/imgWidth;
                imgHeight=imgHeight*product
                imgWidth=imgWidth*product
                that.setData({
                  viewHeight:maxPictureViewHeight,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight,
                  imgWidth:imgWidth
                })
              }else{//图片宽度小于视窗最大宽度
                that.setData({
                  viewHeight:maxPictureViewHeight,
                  viewWidth:imgWidth/2,
                  imgHeight:imgHeight/2,
                  imgWidth:imgWidth/2
                })
              }
            }
          }else{//图片宽高比相差不大,按照原图比例缩放显示
            //console.log("图片宽高比相差不大");
            if(imgWidth > imgHeight){//图片宽度多,图片可能是正方形,宽高比例为1,不过没关系进哪个判断都一样
              //console.log("图片宽度多")
              if(imgWidth>maxPictureViewWidth){//图片宽度比高度多且宽度大于视窗最大宽度,进行等比缩放
                console.log("图片宽度大于视窗最大宽度")
                var product = maxPictureViewWidth/imgWidth;
                
                imgWidth = imgWidth*product
                imgHeight = imgHeight*product;
                that.setData({
                  imgWidth:imgWidth/1.5,
                  imgHeight:imgHeight/1.5,
                  viewWidth:maxPictureViewWidth<imgWidth/1.5?maxPictureViewWidth:imgWidth/1.5,
                  viewHeight:maxPictureViewHeight<imgHeight/1.5?maxPictureViewHeight:imgHeight/1.5
                })
              }else{//图片宽度比高度多判断宽度有没有大于视窗宽度,没大于不进行缩放直接显示
                //console.log("图片宽度不大于视窗最大宽度")
                that.setData({
                  imgWidth:imgWidth/2,
                  imgHeight:imgHeight/2,
                  viewWidth:imgWidth/2,
                  viewHeight:imgHeight/2
                })
              }
            }else{//图片高度多
              // console.log("图片高度多");
              if(imgHeight>maxPictureViewHeight){
                //console.log("图片高度大于视窗最大高度");
                var product = maxPictureViewHeight/imgHeight;
                if(imgWidth>maxPictureViewWidth){
                  product = maxPictureViewHeight/imgHeight;
                }
                imgWidth = (imgWidth*product).toFixed(1);
                imgHeight = (imgHeight*product).toFixed(1);
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:imgWidth,
                  viewHeight:maxPictureViewHeight
                })
              }else{
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:imgWidth,
                  viewHeight:imgHeight
                })
              }
            }
          }
        } 
      }) 
    },
    moved: function(){
      // console.log("组件实例被移动到节点树另一个位置");
    },
    created: function(){
      // console.log("组件实例刚刚被创建时执行");
    },
    ready: function() {
      // console.log("组件布局完成");
    },
  },
  methods:{
    pImg:function(e){
      var imgurl = e.target.dataset.imgurl;
      var list = [];
      list.push(imgurl);
      wx.previewImage({
        urls: list,
      })
    },
  },
  pageLifetimes:{
    hide: function() {
      // console.log("组件所在页面隐藏");
    },
  },
  options: {
      multipleSlots: true,
      addGlobalClass: true
  },
  properties: {
    imgUrl:{
      type: String,
      value: ''
    }
  },
  data: {
    viewWidth:0,
    viewHeight:0,
    imgWidth:0,
    imgHeight:0,
  },
});


WXML

<view style="width:{{viewWidth}}px;height:{{viewHeight}}px;overflow:hidden;border-radius: 5px;">
  <image style="width:{{imgWidth}}px;height:{{imgHeight}}px;" bindtap="pImg" src="{{imgUrl}}" data-imgurl="{{imgUrl}}">image>
view>

WXSS跟JSON啥都没有就不贴

使用方法
在要使用组件的页面对应的JSON文件中进行操作:引用组件

{
  "usingComponents": {
    "adaptivePicture": "/component/adaptivePicture/adaptivePicture"
  }
}

然后在页面中,于要使用自适应图片的位置使用以下标签即可

<adaptivePicture imgUrl="图片地址">adaptivePicture>

注意:此处图片需要js动态获取图片宽高,如果图片地址直接使用io流会报错,想要使用需要用小程序对应接口把图片缓存下来生成链接再填入(接口叫啥忘了,这样做可能有性能隐患,不建议使用)

图片最大视窗区域
在这里插入图片描述
位于组件JS代码最上面的一块,设图片长宽最多能显示多少,如果图片长的一边超过这个视窗,会被缩放/裁剪到这个能容下在视窗内,所以针对场景规划合理的视窗对样式有能多的帮助(正常来讲应该使用用户手机屏幕的宽高,使用百分比来动态控制视窗区域而不是固定的像素,但考虑小程序连横屏都没有,用户屏幕宽度也都差不多,使用场景也差不多,就不画蛇添足了。不会真有人拿平板用小程序吧,不会吧不会吧)

直接看不同比例图片效果图
小程序图片自适应组件 仿微信聊天图片_第1张图片

小程序图片自适应组件 仿微信聊天图片_第2张图片
最后两张图发生了截取,放大后可看到原图
小程序图片自适应组件 仿微信聊天图片_第3张图片
小程序图片自适应组件 仿微信聊天图片_第4张图片

可以看到最终效果跟设想的已经差不多了,样式也可以增加上

通用版图片自适应

JS

Component({
  lifetimes:{
    detached: function() {
      //console.log("组件实例被从页面节点树移除");
    },
    attached: function(){
      //console.log("组件实例进入页面节点树");
      
      var that = this;
      wx.getImageInfo({ 
        src: that.properties.imgUrl,
        success: function (res) {  
          var imgWidth=res.width;
          var imgHeight=res.height;

          var maxPictureViewWidth = 360;//图片视窗最大宽度
          var maxPictureViewHeight = 220;//图片视窗最大高度

          var ratio = imgWidth/imgHeight;

          if(ratio<0.25 || ratio>2.2){//图片宽高比相差过大,可能需要截取
            //判断图片是过高还是过宽
            if(imgWidth>imgHeight){//图片过宽
              if(imgHeight>maxPictureViewHeight){//同时判断图片高度有没有大于视窗最大高度
                //图片过宽且图片高度大于视窗最大高度,依据高度缩减图片比例到视窗可见范围,宽度不管直接靠视窗截取多的部分不显示
                var product = maxPictureViewHeight/imgHeight;
                imgHeight=imgHeight*product
                imgWidth=imgWidth*product
                that.setData({
                  viewHeight:maxPictureViewHeight,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight,
                  imgWidth:imgWidth
                })
              }else{//图片高度小于视窗最大高度
                that.setData({
                  viewHeight:imgHeight,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight,
                  imgWidth:imgWidth
                })
              }
            }else{//图片过高
              if(imgWidth>maxPictureViewWidth){
                //图片过高且图片宽度大于视窗最大宽度,按照宽度缩减图片比例到视窗可见范围,高度不管直接靠视窗截取不显示
                var product = maxPictureViewWidth/imgWidth;
                imgHeight=imgHeight*product
                imgWidth=imgWidth*product
                that.setData({
                  viewHeight:maxPictureViewHeight,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight,
                  imgWidth:imgWidth
                })
              }else{//图片宽度小于视窗最大宽度
                that.setData({
                  viewHeight:maxPictureViewHeight,
                  viewWidth:maxPictureViewWidth,
                  imgHeight:imgHeight,
                  imgWidth:imgWidth
                })
              }
            }
          }else{//图片宽高比相差不大,按照原图比例缩放显示
            //console.log("图片宽高比相差不大");
            if(imgWidth > imgHeight){//图片宽度多,图片可能是正方形,宽高比例为1,不过没关系进哪个判断都一样
              //console.log("图片宽度多")
              if(imgWidth>maxPictureViewWidth){//图片宽度比高度多且宽度大于视窗最大宽度,进行等比缩放
                //console.log("图片宽度大于视窗最大宽度")
                var product = maxPictureViewWidth/imgWidth;
                if(imgHeight>maxPictureViewHeight){
                  product = maxPictureViewHeight/imgHeight;
                }
                imgWidth = (imgWidth*product).toFixed(1);
                imgHeight = (imgHeight*product).toFixed(1);
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:maxPictureViewWidth,
                  viewHeight:maxPictureViewHeight
                })
              }else{//图片宽度比高度多判断宽度有没有大于视窗宽度,没大于不进行缩放直接显示
                //console.log("图片宽度不大于视窗最大宽度")
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:maxPictureViewWidth,
                  viewHeight:maxPictureViewHeight
                })
              }
            }else{//图片高度多
              // console.log("图片高度多");
              if(imgHeight>maxPictureViewHeight){
                //console.log("图片高度大于视窗最大高度");
                var product = maxPictureViewHeight/imgHeight;
                if(imgWidth>maxPictureViewWidth){
                  product = maxPictureViewHeight/imgHeight;
                }
                imgWidth = (imgWidth*product).toFixed(1);
                imgHeight = (imgHeight*product).toFixed(1);
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:maxPictureViewWidth,
                  viewHeight:maxPictureViewHeight
                })
              }else{
                that.setData({
                  imgWidth:imgWidth,
                  imgHeight:imgHeight,
                  viewWidth:maxPictureViewWidth,
                  viewHeight:maxPictureViewHeight
                })
              }
            }
          }
        } 
      }) 
    },
    moved: function(){
      // console.log("组件实例被移动到节点树另一个位置");
    },
    created: function(){
      // console.log("组件实例刚刚被创建时执行");
    },
    ready: function() {
      // console.log("组件布局完成");
    },
  },
  methods:{
    pImg:function(e){
      var imgurl = e.target.dataset.imgurl;
      var list = [];
      list.push(imgurl);
      wx.previewImage({
        urls: list,
      })
    },
  },
  pageLifetimes:{
    hide: function() {
      // console.log("组件所在页面隐藏");
    },
  },
  options: {
      multipleSlots: true,
      addGlobalClass: true
  },
  properties: {
    imgUrl:{
      type: String,
      value: ''
    }
  },
  data: {
    viewWidth:0,
    viewHeight:0,
    imgWidth:0,
    imgHeight:0,
  },
});

WXML跟上面的基本一致

<view style="width:{{viewWidth}}px;height:{{viewHeight}}px;overflow:hidden;border-radius:1%;">
  <image style="width:{{imgWidth}}px;height:{{imgHeight}}px;border-radius:1%;" catchtap="pImg" src="{{imgUrl}}" data-imgurl="{{imgUrl}}">image>
view>

两个区别:聊天室的那个当图片超过视窗显示区域,缩放后会比视窗显示区域还要小1一倍左右
通用版当图片超过视窗显示区域等比缩放后,图片长边宽/高跟视窗显示区域的宽/高一致

所以通用版更适合用在图片本身为主体不需要贴合其他元素的场景(如帖子封面,缩略图)
小程序图片自适应组件 仿微信聊天图片_第5张图片
最后有不同看法,改进方法欢迎留言讨论,希望帖子帮到大家哈哈

你可能感兴趣的:(小程序,前端,javascript,vue.js,前端,小程序,java)