• 小程序经典案例


    1.上拉触底事件

    data: {
        colorList: [],
        isloding: false
      },
    
      getColors() {
        this.setData({
          isloding: true
        })
        // 需要展示 loading 效果
        wx.showLoading({
          title: '数据加载中...'
        })
        wx.request({
          url: 'https://www.escook.cn/api/color',
          method: 'get',
          success: ({ data: res }) => {
            this.setData({
              colorList: [...this.data.colorList, ...res.data]
            })
          },
          complete: () => {
            wx.hideLoading()
            this.setData({
              isloding: false
            })
          }
        })
      },
      onReachBottom: function () {
      	//isloading 节流阀
        if (this.data.isloding) return
        this.getColors()
      },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2.分页判断是否还有 数据

    页码值 * 每页显示多少条数据 >= 总数据条数
    
    • 1

    page * pageSize >= total 代表没数据了

    3.自定义tabber

    官网

    4.封装request

    config.js
    // 配置服务器相关信息
    export default {
      host: 'http://localhost:3000',
      mobileHost: 'https://zhiyongzaixian123123.utools.club'
    }
    request.js
    // 发送ajax请求
    /*
    * 1. 封装功能函数
    *   1. 功能点明确
    *   2. 函数内部应该保留固定代码(静态的)
    *   3. 将动态的数据抽取成形参,由使用者根据自身的情况动态的传入实参
    *   4. 一个良好的功能函数应该设置形参的默认值(ES6的形参默认值)
    * 2. 封装功能组件
    *   1. 功能点明确
    *   2. 组件内部保留静态的代码
    *   3. 将动态的数据抽取成props参数,由使用者根据自身的情况以标签属性的形式动态传入props数据
    *   4. 一个良好的组件应该设置组件的必要性及数据类型
    *     props: {
    *       msg: {
    *         required: true,
    *         default: 默认值,
    *         type: String
    *       }
    *     }
    *
    * */
    import config from './config'
    export default  (url, data={}, method='GET') => {
      return new Promise((resolve, reject) => {
        // 1. new Promise初始化promise实例的状态为pending
        wx.request({
          url: config.host + url,
          data,
          method,
          header: {
            cookie: wx.getStorageSync('cookies')?wx.getStorageSync('cookies').find(item => item.indexOf('MUSIC_U') !== -1):''
          },
          success: (res) => {
            // console.log('请求成功: ', res);
            if(data.isLogin){// 登录请求
              // 将用户的cookie存入至本地
              wx.setStorage({
                key: 'cookies',
                data: res.cookies
              })
            }
            resolve(res.data); // resolve修改promise的状态为成功状态resolved
          },
          fail: (err) => {
            // console.log('请求失败: ', err);
            reject(err); // reject修改promise的状态为失败状态 rejected
          }
        })
      })
      
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    5.配置网络请求

    由于平台的限制,小程序项目中不支持 axios,而且原生的 wx.request() API 功能较为简单,不支持拦截器等全局定制的功能。因此,建议在 uni-app 项目中使用 @escook/request-miniprogram 第
    三方包发起网络数据请求
    官方文档

    //main.js
    	import { $http } from '@escook/request-miniprogram'
    	uni.$http = $http 
    	// 配置请求根路径
    	$http.baseUrl = 'https://www.uinav.com' 
    // 请求开始之前做一些事情 
    	$http.beforeRequest = function (options) {
    	    uni.showLoading({ title: '数据加载中...', }) 
    	}
    // 请求完成之后做一些事情 
    	$http.afterRequest = function () { 
    		uni.hideLoading() 
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    6.滚动条切换左侧tab,上下滚动条回到顶部


    :scroll-top="scrollTop
    点击tab的时候加上
    this.scrollTop = this.scrollTop === 0 ? 1 : 0

    7.输入框防抖

    input(e) {
    // 清除 timer 对应的延时器
    clearTimeout(this.timer)
    // 重新启动一个延时器,并把 timerId 赋值给 this.timer
    this.timer = setTimeout(() => {
    // 如果 500 毫秒内,没有触发新的输入事件,则为搜索关键词赋值
    this.kw = e.value console.log(this.kw)
    }, 500)
    }

    8.数组实操的简单方案

    转为set增删
    // 1. 将 Array 数组转化为 Set 对象 
    const set = new Set(this.historyList) 
    // 2. 调用 Set 对象的 delete 方法,移除对应的元素 
    set.delete(this.kw) 
    // 3. 调用 Set 对象的 add 方法,向 Set 中添加元素 
    set.add(this.kw) 
    // 4. 将 Set 对象转化为 Array 数组 
    this.historyList = Array.from(set)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    9.下拉刷新结合回调的经典案例

    // 下拉刷新的事件 
    onPullDownRefresh() { 
    	// 1. 重置关键数据 
    	this.queryObj.pagenum = 1 
    	this.total = 0 
    	this.isloading = false 
    	this.goodsList = [] 
    	// 2. 重新发起请求 
    	this.getGoodsList(() => uni.stopPullDownRefresh()) 
    }
    
    // 获取商品列表数据的方法 
    async getGoodsList(cb) { 
    	this.isloading = true 
    	const { data: res } = await uni.$http.get('/api/public/v1/goods/search', this.queryObj) 
    	this.isloading = false 
    	// 只要数据请求完毕,就立即按需调用 cb 回调函数 
    	cb && cb() 
    	if (res.meta.status !== 200) return uni.$showMsg() 
    	this.goodsList =[...this.goodsList,...res.message.goods] 
    	this.total = res.message.total 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    10.登录、需要登录的页面跳转后返回

    1.为登录的 button 按钮绑定 open-type=“getUserInfo” 属性,表示点击按钮时,希望获取用户的基本信息
    2.调用uni.login()获取code
    3.利用code和用户信息调用登录接口获取token
    4.把token和用户信息存在vuex和缓存中
    代码

    <template>
      <view class="login-container">
        <uni-icons type="contact-filled" size="100" color="#AFAFAF"></uni-icons>
        <button type="primary" class="btn-login" open-type="getUserInfo" @getuserinfo="getUserInfo">一键登录</button>
        <text class="tips-text">登录后尽享更多权益</text>
      </view>
    </template>
    
    <script>
      import { mapMutations, mapState } from 'vuex'
    
      export default {
        data() {
          return {
    
          };
        },
        computed: {
          ...mapState('m_user', ['redirectInfo'])
        },
        methods: {
          ...mapMutations('m_user', ['updateUserInfo', 'updateToken', 'updateRedirectInfo']),
          // 用户授权之后,获取用户的基本信息
          getUserInfo(e) {
            console.log(e)
    
            if (e.detail.errMsg === 'getUserInfo:fail auth deny') return uni.$showMsg('您取消了登录授权!')
    
            console.log(e.detail.userInfo)
            this.updateUserInfo(e.detail.userInfo)
    
            this.getToken(e.detail)
          },
          async getToken(info) {
            // 获取 code 对应的值
            const [err, res] = await uni.login().catch(err => err)
    
            if (err || res.errMsg !== 'login:ok') return uni.$showMsg('登录失败!')
    
            // 准备参数
            const query = {
              code: res.code,
              encryptedData: info.encryptedData,
              iv: info.iv,
              rawData: info.rawData,
              signature: info.signature
            }
    
            const { data: loginResult } = await uni.$http.post('/api/public/v1/users/wxlogin', query)
            if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
    
            // 直接把 token 保存到 vuex 中
            this.updateToken(loginResult.message.token)
            this.navigateBack()
          },
          navigateBack() {
            if (this.redirectInfo && this.redirectInfo.openType === 'switchTab') {
              uni.switchTab({
                url: this.redirectInfo.from,
                complete: () => {
                  this.updateRedirectInfo(null)
                }
              })
            }
          }
        }
      }
    </script>
    
    vuex
    store.js
    import Vue from 'vue'
    import Vuex from 'vuex'
    import moduleCart from '@/store/cart.js'
    import moduleUser from '@/store/user.js'
    Vue.use(Vuex)
    const store = new Vuex.Store({
      modules: {
        'm_user': moduleUser
      }
    })
    export default store
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    user.js
    export default {
      // 开启命名空间
      namespaced: true,
    
        token: uni.getStorageSync('token') || '',
        // 用户的信息对象
        userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}'),
        // 重定向的 Object 对象
        redirectInfo: null
      }),
    
      // 方法
      mutations: {
        updateUserInfo(state, userinfo) {
          state.userinfo = userinfo
          this.commit('m_user/saveUserInfoToStorage')
        },
        saveUserInfoToStorage(state) {
          uni.setStorageSync('userinfo', JSON.stringify(state.userinfo))
        },
        updateToken(state, token) {
          state.token = token
          this.commit('m_user/saveTokenToStorage')
        },
        saveTokenToStorage(state) {
          uni.setStorageSync('token', state.token)
        },
        updateRedirectInfo(state, info) {
          state.redirectInfo = info
          console.log(state.redirectInfo)
        }
      },
    
      getters: {
        // 收货地址
        addstr(state) {
          if (!state.address.provinceName) return ''
          return state.address.provinceName + state.address.cityName + state.address.countyName + state.address.detailInfo
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    退出登录

    清空token和用户信息
    this.updateUserInfo({}) 
    this.updateToken('')
    
    • 1
    • 2
    • 3

    11.延迟3秒跳转登录界面

    export default {
        data() {
          return {
            // 倒计时的秒数
            seconds: 3,
            // 定时器的 Id
            timer: null
          };
        },
        methods: {
          // 延时导航到 my 页面
          delayNavigate() {
            this.seconds = 3
            this.showTips(this.seconds)
            this.timer = setInterval(() => {
              this.seconds--
              if (this.seconds <= 0) {
                clearInterval(this.timer)
                uni.switchTab({
                  url: '/pages/my/my',
                  success: () => {
                    this.updateRedirectInfo({
                      openType: 'switchTab',
                      from: '/pages/cart/cart'
                    })
                  }
                })
                return
              }
              this.showTips(this.seconds)
            }, 1000)
          },
          // 展示倒计时的提示消息
          showTips(n) {
            uni.showToast({
              icon: 'none',
              title: '请登录后再结算!' + n + '秒之后自动跳转到登录页',
              mask: true,
              duration: 1500
            })
          }
        }
      }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    12.微信支付

    前提
    在请求头中添加 Token 身份认证的字段
    原因说明:只有在登录之后才允许调用支付相关的接口,所以必须为有权限的接口添加身份认证的请求头字段
    实现:
    main.js拦截器处理

    import Vue from 'vue'
    import App from './App'
    import store from '@/store/store.js'
    // 导入网络请求的包
    import { $http } from '@escook/request-miniprogram'
    uni.$http = $http
    // 请求拦截器
    $http.beforeRequest = function(options) {
      uni.showLoading({
        title: '数据加载中...'
      })
      // 判断当前请求的是否为有权限的接口
      if (options.url.indexOf('/my/') !== -1) {
        options.header = {
          Authorization: store.state.m_user.token
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    流程

    1. 创建订单
      请求创建订单的 API 接口:把(订单金额、收货地址、订单中包含的商品信息)发送到服务器;
      服务器响应的结果:订单编号
      2.订单预支付
      请求订单预支付的 API 接口:把(订单编号)发送到服务器
      服务器响应的结果:订单预支付的参数对象 ,里面包含了订单支付相关的必要参数
      3.发起微信支付
      调用 uni.requestPayment() 这个 API,发起微信支付;把步骤 2 得到的 “订单预支付对象” 作为参数传递给 uni.requestPayment() 方法
      监听 uni.requestPayment() 这个 API 的 success , fail ,complete 回调函数
      4.调接口查看订单支付状态,来确认支付是否成功。

    代码:

    // 用户点击了结算按钮
          settlement() {
            if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')
            if (!this.addstr) return uni.$showMsg('请选择收货地址!')
            // if (!this.token) return uni.$showMsg('请先登录!')
            if (!this.token) return this.delayNavigate()
            this.payOrder()
          },
          async payOrder() {
            // 1. 创建订单
            // 1.1 组织订单的信息对象
            const orderInfo = {
              // order_price: this.checkedGoodsAmount,
              order_price: 0.01,
              consignee_addr: this.addstr,
              goods: this.cart.filter(x => x.goods_state).map(x => ({
                goods_id: x.goods_id,
                goods_number: x.goods_count,
                goods_price: x.goods_price
              }))
            }
    
            // 1.2 发起请求创建订单
            const { data: res } = await uni.$http.post('/api/public/v1/my/orders/create', orderInfo)
            if (res.meta.status !== 200) return uni.$showMsg('创建订单失败!')
    
            // 1.3 得到服务器响应的“订单编号”
            const orderNumber = res.message.order_number
    
            // 2. 订单预支付
            // 2.1 发起请求获取订单的支付信息
            const { data: res2 } = await uni.$http.post('/api/public/v1/my/orders/req_unifiedorder', { order_number: orderNumber })
            // 2.2 预付订单生成失败
            if (res2.meta.status !== 200) return uni.$showMsg('预付订单生成失败!')
            // 2.3 得到订单支付相关的必要参数
            const payInfo = res2.message.pay
    
            // 3. 发起微信支付
            // 3.1 调用 uni.requestPayment() 发起微信支付
            const [err, succ] = await uni.requestPayment(payInfo)
            // 3.2 未完成支付
            if (err) return uni.$showMsg('订单未支付!')
            // 3.3 完成了支付,进一步查询支付的结果
            const { data: res3 } = await uni.$http.post('/api/public/v1/my/orders/chkOrder', { order_number: orderNumber })
            // 3.4 检测到订单未支付
            if (res3.meta.status !== 200) return uni.$showMsg('订单未支付!')
            // 3.5 检测到订单支付完成
            uni.showToast({
              title: '订单支付完成!',
              icon: 'success'
            })
          },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    13.uniapp滑动删除

    主要uniapp官网使用uni-swipe-action 滑动事件

    //滑动删除,
    <uni-swipe-action>
          <block v-for="(goods, i) in cart" :key="i">
            <uni-swipe-action-item :options="options" @click="swipeItemClickHandler(goods)">
              <my-goods :goods="goods" :show-radio="true" :show-num="true" @radio-change="radioChangeHandler" @num-change="numberChangeHandler"></my-goods>
            </uni-swipe-action-item>
          </block>
        </uni-swipe-action>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    14.video列表显示

    wxml
    <scroll-view
          scroll-y
          class="videoScroll"
          refresher-enabled
          bindrefresherrefresh="handleRefresher"
          refresher-triggered="{{isTriggered}}"
          bindscrolltolower="handleToLower"
      >
        <view class="videoItem" wx:for="{{videoList}}" wx:key="id">
          <video
              src="{{item.data.urlInfo.url}}"
              bindplay="handlePlay"
              id="{{item.data.vid}}"
              poster="{{item.data.coverUrl}}"
              class="common"
              object-fit="cover"
              wx:if='{{videoId === item.data.vid}}'
              bindtimeupdate="handleTimeUpdate"
              bindended="handleEnded"
          ></video>
    
          <!-- 性能优化:使用image图片代替video标签 (video,多个可能一直加载不出来)-->
          <image wx:else bindtap="handlePlay" id="{{item.data.vid}}" class="common" src="{{item.data.coverUrl}}"></image>
    
    
          <view class="content">{{item.data.title}}</view>
    
          <view class="footer">
            <image class="avatar" src="{{item.data.creator.avatarUrl}}"></image>
            <text class="nickName">{{item.data.creator.nickname}}</text>
            <view class="comments_praised">
              <text class="item">
                <text class="iconfont icon-buoumaotubiao15"></text>
                <text class="count">{{item.data.praisedCount}}</text>
              </text>
              <text class="item">
                <text class="iconfont icon-pinglun1"></text>
                <text class="count">{{item.data.commentCount}}</text>
              </text>
              <button open-type="share" class="item btn">
                <text class="iconfont icon-gengduo"></text>
              </button>
            </view>
          </view>
        </view>
      </scroll-view>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    js文件
    Page({
      /**
       * 页面的初始数据
       */
      data: {
        navId: '', // 导航的标识
        videoList: [], // 视频列表数据
        videoId: '', // 视频id标识
        videoUpdateTime: [], // 记录video播放的时长
        isTriggered: false, // 标识下拉刷新是否被触发
      },
    
      // 点击播放/继续播放的回调
      handlePlay(event){
        /*
          问题: 多个视频同时播放的问题
        * 需求:
        *   1. 在点击播放的事件中需要找到上一个播放的视频
        *   2. 在播放新的视频之前关闭上一个正在播放的视频
        * 关键:
        *   1. 如何找到上一个视频的实例对象
        *   2. 如何确认点击播放的视频和正在播放的视频不是同一个视频
        * 单例模式:
        *   1. 需要创建多个对象的场景下,通过一个变量接收,始终保持只有一个对象,
        *   2. 节省内存空间
        * */
        
        let vid = event.currentTarget.id;
        // 关闭上一个播放的视频
        // this.vid !== vid && this.videoContext && this.videoContext.stop();
        // if(this.vid !== vid){
        //   if(this.videoContext){
        //     this.videoContext.stop()
        //   }
        // }
        // this.vid = vid;
        
        // 更新data中videoId的状态数据
        this.setData({
          videoId: vid
        })
        // 创建控制video标签的实例对象
        this.videoContext = wx.createVideoContext(vid);
        // 判断当前的视频之前是否播放过,是否有播放记录, 如果有,跳转至指定的播放位置
        let {videoUpdateTime} = this.data;
        let videoItem = videoUpdateTime.find(item => item.vid === vid);
        if(videoItem){
          this.videoContext.seek(videoItem.currentTime);
        }
        this.videoContext.play();
        // this.videoContext.stop();
      },
      
      // 监听视频播放进度的回调
      handleTimeUpdate(event){
        let videoTimeObj = {vid: event.currentTarget.id, currentTime: event.detail.currentTime};
        let {videoUpdateTime} = this.data;
        /*
        * 思路: 判断记录播放时长的videoUpdateTime数组中是否有当前视频的播放记录
        *   1. 如果有,在原有的播放记录中修改播放时间为当前的播放时间
        *   2. 如果没有,需要在数组中添加当前视频的播放对象
        *
        * */
        let videoItem = videoUpdateTime.find(item => item.vid === videoTimeObj.vid);
        if(videoItem){ // 之前有
          videoItem.currentTime = event.detail.currentTime;
        }else { // 之前没有
          videoUpdateTime.push(videoTimeObj);
        }
        // 更新videoUpdateTime的状态
        this.setData({
          videoUpdateTime
        })
      },
      
      // 视频播放结束调用的回调
      handleEnded(event){
        // 移除记录播放时长数组中当前视频的对象
        let {videoUpdateTime} = this.data;
        videoUpdateTime.splice(videoUpdateTime.findIndex(item => item.vid === event.currentTarget.id), 1);
        this.setData({
          videoUpdateTime
        })
      },
      
      // 自定义下拉刷新的回调: scroll-view
      handleRefresher(){
        console.log('scroll-view 下拉刷新');
        // 再次发请求,获取最新的视频列表数据
        this.getVideoList(this.data.navId);
      },
      
      // 自定义上拉触底的回调 scroll-view
      handleToLower(){
        console.log('scroll-view 上拉触底');
        // 数据分页: 1. 后端分页, 2. 前端分页
        }
     })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    15.动画案例

    .discAnimation {
      animation: disc 4s linear infinite;
      animation-delay: 1s;
    }
    
    /*
      @keyframes: 设置动画帧
        1) from to
          - 使用于简单的动画,只有起始帧和结束帧
          - 北京 - 上海  直达
        2) 百分比
          - 多用于复杂的动画,动画不止两帧
          - 北京 - 上海 ---> 北京 -- 天津 --- 深圳 --- 上海
          - 0% - 100%, 可以任意拆分
    
    */
    
    @keyframes disc {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    16.分享

    wxml按钮
    <button open-type="share" class="item btn">
     <text class="iconfont icon-gengduo"></text>
    </button>
    js
    
      /**
       * 用户点击右上角分享
       */
      onShareAppMessage: function ({from}) {
        console.log(from);
        if(from === 'button'){
          return {
            title: '来自button的转发',
            page: '/pages/video/video',
            imageUrl: '/static/images/nvsheng.jpg'
          }
        }else {
          return {
            title: '来自menu的转发',
            page: '/pages/video/video',
            imageUrl: '/static/images/nvsheng.jpg'
          }
        }
        
      }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
  • 相关阅读:
    2024.6.14刷题记录-KMP记录
    智能合约自动化工具:示例指南
    SpringBoot 中间件设计和开发
    【INTEL(ALTERA)】Nios II软件开发人员手册中设计位置的错误示例
    面试被问Redis主从复制不会答?这13张图让你彻底弄明白
    【RTOS学习】软件定时器 | 中断处理
    报错解决:Process finished with exit code -1073741819 (0xC0000005)
    【boot loader】Aurix TC3xx用户手册解读--CAN Bootstrap loader
    Linux磁盘分区与挂载
    win10-wsl安装卸载以及软件安装
  • 原文地址:https://blog.csdn.net/qq_38799387/article/details/126128960