小程序一些技巧分享

CSS 背景图片百分比及应用

适用于rpx雪碧图标中背景图片精准定位

百分比计算公式

任何CSS属性值为百分比时,都需要根据某个参考值进行计算,搞明白这个参考值是什么,理解就容易多了。

标准规定:background-position:percent的参考值x为:百分比为background-position-x的值 = (背景在雪碧中的左边宽度)/(容器宽度 – 背景图片宽度)*100%。

举个例子:

百度分享中有四个图标(大小设置是0.72rem0.72rem),我把这四个小图标扣到一张大小为(0.72rem3.65rem)的背景图,宽度跟图标是一样的,高度则为四个图片+1倍的高度,主要是为了后面的高度百分比成正比。

那么我们可以得到这张小图片的样式为:

.icon{
    width:.72rem;
    height:.72rem;
    backgorund:url(img/sprite.png) no-repeat center top;
    background-size:100%; //cover
    background-position:0 0
}
.icon1{
    backgorund-position:0 0;
}
.icon2{
    background-position:0 -73/(73 - 365)*100%=25%
}
.icon3{
    background-position:0 -146/(73-365)*100%=50%
}
.icon4{
    background-position:0 -219/(73-365)*100%=75%
}

最关键是要理解上面这个计算方式。其实我们如果换另一大家都熟悉的常见定位就清楚了。

.icon2{
    background-position:0 -0.73rem; (这个距离就是图标距离背景图片中的偏离值) 
}

无限加载的两种处理

  1. 利用scroll-view组件自带的滑到底部触发函数
  2. 利用page函数上拉触底onReachBottom

第一种

<scroll-view bindscrolltolower="scrollBottom"></scroll-view>
Page({
    scrollBottom:function(){
        console.log('滚动到底部了');
    }
})

第二种

Page({
    onReachBottom:function(){
        console.log('滚动到底部了');
    }
})

swiper多栏切换无限加载

  1. 使用swiper+scroll-view结合
  2. 使用touch事件

第一种

<swiper>
<block for="{{list}}">
<swiper-item>
<scroll-view class="list" scroll-y="true" bindscrolltolower="loadMoreList">
<block for="{{list[item]}}">
<view class="item">
</view>
</block>
</scroll-view>
</swiper-item>
</block>
</swiper>

第二种

https://github.com/we-plugin/we-swiper

隐藏滚动条

内外层固定宽高,scroll-view添加padding或者把高度设为比外层的高度高。

<view class="tab">
    <view class="tab-inner">
        <scroll-view class="scroll-bangdan" scroll-x="true" scroll-left="{{scrollLeft}}">
            <view class="ctrl" style="width:1054rpx">
                <block wx:for="{{nav}}" wx:key="{{item.columnId}}">
                    <view class="toc{{current==index?' cur':''}}" bindtap="changeTab" data-index="{{index}}">
                        <view class="text">
                            <text>{{item.columnName}}</text>
                        </view>
                    </view>
                </block>
            </view>
        </scroll-view>
        <view class="mask"></view>
    </view>
</view>

CSS 代码

.tab {
    /* position: fixed;
    left: 0;
    top: 0; */
    border-bottom: 1px solid #f8f8f8;
    background: #fff;
    z-index: 1;
    width: 100%;
    height: 100rpx;
    line-height: 100rpx;
}

.tab-inner {
    height: 100rpx;
    overflow: hidden;
}
.scroll-bangdan {
    width: 100%;
    position: relative;
    height: 130rpx;
}

.tab .ctrl {
    display: -webkit-box;
    display: -moz-box;
    display: -ms-flexbox;
    display: -webkit-flex;
    display: flex;
    flex-flow: row wrap;
    -webkit-flex: row wrap;
    font-size: 28rpx;
    height: 100rpx;
    line-height: 100rpx;
}

第二种直接加下面的代码:

/* 去掉默认滚动条 */
::-webkit-scrollbar {
  width: 0;
  height: 0;
  color: transparent;
}

订阅发布模式

支持先订阅后发布,以及先发布后订阅

var Event = (function() {
  var clientList = {},
    pub,
    sub,
    remove;

  var cached = {};

  sub = function(key, fn) {
    if (!clientList[key]) {
      clientList[key] = [];
    }
    // 使用缓存执行的订阅不用多次调用执行
    cached[key + "time"] == undefined ? clientList[key].push(fn) : "";
    if (cached[key] instanceof Array && cached[key].length > 0) {
      //说明有缓存的 可以执行
      fn.apply(null, cached[key]);
      cached[key + "time"] = 1;
    }
  };
  pub = function() {
    var key = Array.prototype.shift.call(arguments),
      fns = clientList[key];
    if (!fns || fns.length === 0) {
      //初始默认缓存
      cached[key] = Array.prototype.slice.call(arguments, 0);
      return false;
    }

    for (var i = 0, fn; (fn = fns[i++]); ) {
      // 再次发布更新缓存中的 data 参数
      cached[key + "time"] != undefined
        ? (cached[key] = Array.prototype.slice.call(arguments, 0))
        : "";
      fn.apply(this, arguments);
    }
  };
  remove = function(key, fn) {
    var fns = clientList[key];
    // 缓存订阅一并删除
    var cachedFn = cached[key];
    if (!fns && !cachedFn) {
      return false;
    }
    if (!fn) {
      fns && (fns.length = 0);
      cachedFn && (cachedFn.length = 0);
    } else {
      if (cachedFn) {
        for (var m = cachedFn.length - 1; m >= 0; m--) {
          var _fn_temp = cachedFn[m];
          if (_fn_temp === fn) {
            cachedFn.splice(m, 1);
          }
        }
      }
      for (var n = fns.length - 1; n >= 0; n--) {
        var _fn = fns[n];
        if (_fn === fn) {
          fns.splice(n, 1);
        }
      }
    }
  };
  return {
    pub: pub,
    sub: sub,
    remove: remove
  };
})();
// app.js
App({
  onLaunch: function(e) {
    // 注册 storage,这是第二条
    wx.Storage = Storage;
    // 注册发布订阅模式
    wx.yue = Event;
  }
});

实际应用

// 添加收货地址页面订阅
 onLoad: function (options) {
        wx.yue.sub("addAddress", function (data) {
            y.setData({
                addAddress: data
            })
        })
 }
/**
 * 生命周期函数--监听页面隐藏
 */
 onHide: function () {
    // 取消多余的事件订阅
    wx.Storage.removeItem("addAddress");
},
 onUnload: function () {
    // 取消多余的事件订阅
    wx.yue.remove("addAddress");
}
// 传递地址页面获取好数据传递
wx.yue.pub("addAddress", data.info);
// 补充跳转返回

注意:使用完成数据后要注意卸载,在页面被关闭时操作

Storage

const Storage = {
  setItem: function(key, obj, callback) {
    wx.setStorage({
      key: key,
      data: obj,
      success: callback || function() {}
    });
  },
  getItem: function(key) {
    return wx.getStorageSync(key);
  },
  removeItem: function(key) {
    wx.removeStorage({
      key: key
    });
  }
};

async await

使用runtime.js,使小程序支持 async await,拷贝文件至项目目录下。

const regeneratorRuntime = require("../../utils/runtime.js");
Page({
  shopCartInit() {
    var y = this;
    // 拿到商铺位置信息再去渲染购物计算当前的address符合不符合规定
    var showCartList = function() {
      // 显示全局的地址信息
      var globalAddress = wx.Storage.getItem("globalAddress");
      if (globalAddress) {
        y.setData({
          globalAddress: globalAddress,
          addr_id: globalAddress.id
        });
        y.calculateDistance(
          qqmapsdk,
          globalAddress.latitude,
          globalAddress.longitude
        );
      } else {
        y.setData({
          globalAddress: {}
        });
      }
    };
    // await 等待获取商铺位置信息
    async function getShopPosTionMsg() {
      await util.promiseRequest(api.merchant_addr, {}).then(res => {
        var data = res.data.response_data.lists[0];
        y.setData({
          shop_lat: data.latitude, // 商铺纬度
          shop_lng: data.longitude, // 商铺经度
          peiSongFanWei: data.scope // 配送范围
        });
      });
    }

    async function initData() {
      await getShopPosTionMsg();
      await showCartList();
      util.closeLoading();
      y.setData({
        loading: false
      });
    }
    // 开始执行
    initData();
  }
});

promise

引入github上的一个库,wx.promise

// 演示 wxPromise 的能力
wx.showLoading({
  title: '加载中',
  mask: true
})
wx.pro.request({
  url: 'https://cnodejs.org/api/v1/topics',
  data: {},
  method: 'GET',
  header: {'content-type': 'application/json'}
}).then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
}).finally(() => {
  wx.hideLoading()
})

关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流