zweizhao.github.io

项目,文章,随笔,博客,Markdown,个人网站,人生感悟。

View on GitHub

动漫二次元图片分享保存工具

这是基于之前发布 Google Play 的 iPixel Wallpaper APP 修改而来,本就不是为了运营,只是录做视频与文章使用,有兴趣的可以看看,当前还有 Web 版本的,目前都在同步更新中。

本文先写写首页的内容吧,其他页面后续更新。

  1. 标签列表
  2. 图片列表

最近滚回原生党,除非巨型项目,否则都会使用原生,包括但不限于小程序。

标签列表

效果:

标签列表预览

相关代码:

html

<view id="nav">
  <button class="item"
    bindtap='tapButton'
    wx:for="" wx:key=""
    data-index="">
    
  </button>
</view>

这里应该使用 scroll-view,然后设置 x 方向滚动。因为当时写顺手了,就弄了个 view,然后手动 css 设置 x 滚动,据说性能是差一点,但是既然代码这样,就先这样放出来,当个反面教材。

然后,wx:for 与 wx:key 是一对冤家,基本都是一起出现,for 是循环好理解,key 是避免 for 生成的节点不唯一导致的一些 bug,这里不细说,简单记下就是一起出现即可。对了,key 要唯一,所以一般来说,index 是最好的小伙伴。

bindtap 就是 事件的“别名”了,但道理上是不一样的,这样记问题不大。

data-index 就是绑定的数据,可以在 js 里面类似 DOM data 属性一样获取到。

“” 就是获取 js 里面声明的 data 绑定数据了,这个 data 数据是可以自动绑定的。

其他就是跟 HTML 区别不大了,除了标签名之外。

css

#nav {
  white-space: nowrap;
  overflow-x: scroll;
  height: 64rpx;
}

#nav .item {
  display: inline;
  min-width: 120rpx;
  line-height: 64rpx;
  margin: 8rpx 0;
  color: grey;
}

css 没什么说得,不说样式丑就行。

js

//index.js
//获取应用实例
const app = getApp()
const AV = app.globalData.AV

Page({
  data: {
    classes: ['Asuna'],
    images: [],
  },
  // 获取标签列表
  getClasses() {
    new AV.Query('CLASSES')
      .find()
      .then(res => {
        const classes = Array.from(res, item => item.get('className'))
        this.setData({
          classes: classes
        })
      })
      .catch(console.error)
  },
  // 点击标签
  tapButton(e) {
    const className = this.data.classes[e.target.dataset.index]
    this.getImages(className)
  },
  onReady() {
    this.getClasses()
  },
})

前两行是获取小程序实例,以及第三方相关,这里第三方就是之前项目提供数据源的地方,不打广告,理解成后台获取资源就行,具体获取方式在 js 里面,也不要深究。

onReady 是当前页加载前,此时获取数据,因为是异步,所以应该不会出现渲染失败。注意是应该,个人建议还是放在 onLoad 里面, 我这里因为是第三方官网的 Demo 拷贝,就没有去改它,等下有自己写的,我都是放在 onLoad。

this.setData({
  classes: classes
})

React 小伙伴应该比较熟悉,这里就是小程序数据绑定修改方式,这种方式修改可以使得 wxml(HTML)里面“”绑定的视图同步更新。

没错,this.data.x = y 可以修改但是不能同步更新,明白了?

注意:与 React 不同,这玩意是同步的,不是 React 那种异步,所以不存在回调处理。

tapButton 就是绑定在标签上的点击事件,与获取图片列表有关。

那么,到这里,整个标签列表就完成了。


图片列表

效果:

图片列表预览

相关代码:

html

<scroll-view id='main' scroll-y>
  <view class='img-container'
    wx:for=""
    wx:key="index"
    wx:for-item="url"
    data-index=""
    bindtap='tapImage'>
    <image src="" lazy-load mode="aspectFit"></image>
  </view>
</scroll-view>

wx:for-item 指定循环遍历使用的 item 名,配套的有 wx:for-index 指定循环遍历使用的 index 名。使得,默认不写就是 item、index。这里为了语义清楚点,就设置成 url 了。

lazy-load 说是可以懒加载,不知道是不是我图量少,目前没有体验出来。还是说我用错了?

至于 mode="aspectFit 就是个人喜欢小程序的一点了,图片缩放处理已经被框架处理好,不要自己再去配置 width、height、自动缩放等等了。具体可以看官方说明:小程序组件——image

scroll-view 就不用说了,也是框架提供好的滚动视图,设置下具体宽高,在配置想方向就可以了:scroll-yscroll-x

css

#main {
  height: calc(100% - 64rpx);
}

.img-container {
  display: inline-block;
  width: 47%;
  height: 400rpx;
}

.img-container:nth-child(2n + 1) {
  margin-left: 2%;
  margin-right: 1%;
}

.img-container:nth-child(2n) {
  margin-left: 1%;
  margin-right: 2%
}

image {
  width: 100%;
  height: 100%;
}

由于我们是竖直方向滚动,所以设置的 #main 的高。原来是想像 APP 一样实现瀑布流,但是发现 Grid 小程序好像不支持?就只能退而求其次的选择比较 low 的常规两列布局了。具体 Grid 的瀑布流,效果如下:

瀑布流效果

小程序的,哎……

中间 img-container,一样是夹层,方便以后拓展。

js

//index.js
//获取应用实例
const app = getApp()
const AV = app.globalData.AV

Page({
  data: {
    classes: ['Asuna'],
    images: [],
  },
  // 获取标签下图片列表
  getImages(className) {
    this.setData({
      images: [],
      title: className
    })

    wx.showLoading({
      title: "获取列表数据中……"
    })

    new AV.Query(className)
      .find()
      .then(res => {
        wx.hideLoading()
        this.setData({
          images: Array.from(res, item => item.get('url'))
        })
      })
      .catch(console.error)
  },
  onLoad() {
    this.getImages(this.data.classes[0])
  },
})

注意,默认 data 里面有一个标签,就是为了防止第一次进来没东西。

这个就相对简单不说那么具体,提一下需要注意的优化点:

  1. 点击标签切换的时候,要先清空图片数组,否则会出现延时或者干脆显示不匹配的问题。
  2. 既然是异步交互,那还是带上菊花转吧 wx.showLoading,成功后记得关掉 wx.hideLoading

小结

  1. this.setData 是驱动数据与视图更新的要点。
  2. 记得修改数组前清空下数组。
  3. 用户交互的菊花转是个好东西。
  4. 小程序,简单的一塌糊涂。

嗯,是时候关注一波了。