在小程序实现原生相册的效果
发布于 2 年前 作者 yang09 2508 次浏览 来自 分享

相册在日常生活中经常使用到,如手机自带相册、朋友圈、商品展示图、评论贴图等等,都经常用到相册的能力。

👇下面演示 iOS 原生相册、朋友圈等相册使用效果,我们可以看到图片切换非常顺滑,视觉焦点不变。

😭 但是在小程序中,页面切换会有明显的切换感。用户焦点会丢失,缺少视觉关联性。

共享元素

🔥 为了丰富用户交互效果、提升用户体验、增强视觉关联性,小程序支持了页面间的共享元素

下图展示有无共享元素的页面切换效果,可以看出使用共享元素之后,转场动画更灵活

共享元素 经常作用在图片上,例如上面示例中的相册效果,是那么共享元素动画要怎么实现呢?

在页面跳转时,两个页面 key 相同的 share-element 组件则会产生飞跃的过渡效果

在上一篇文章中,我们学习了 页面转场动画,共享元素动画跟页面转场动画是类似的,同样是在页面切换间的动画。

动画进度、时间 与 路由进度、时间保持一致(非自定义路由也支持共享元素动画)

在共享元素飞跃的过程中,前后页面图片的裁剪方式(mode) 可能不一致

这种情况下容易导致图片突然跳变,所以我们需要在飞跃的过程中改变图片的大小来保证平滑飞跃

在共享元素动画进行的过程中,share-element 可以收到 onFrame 表示动画帧回调

我们可以在帧回调中处理内部元素的显示

例如:我们这里通过在帧回调中改变图片宽高来达到平滑飞跃的效果

// .wxml
<share-element key="binnie" onFrame="onFrame">
  <image bind:load="onImageLoad" />
</share-element>

// .js
// 初始化
attached() { 
  this.aspectRatio = shared(0)
  this.curRect = shared(undefined)

  // 绑定 worklet 动画
  this.applyAnimatedStyle('.img', () => {
    'worklet'
    const curRect = this.curRect.value

    return {
      left: `${curRect.left}px`,
      top: `${curRect.top}px`,
      width: `${curRect.width}px`,
      height: `${curRect.height}px`
    }
  })
},

// 获取图片初始宽高比
onImageLoad(e) {
  const { width, height } = e.detail
  this.aspectRatio.value = width / height
},

// 动画帧回调,调整图片大小
onFrame(data) {
  'worklet'
  // 当前帧容器的宽高、进度等信息
  const { begin, end, progress, direction } = data
  
  ...

  // 根据图片初始宽高比、共享元素容器、动画进度等计算出变化过程中的值
  this.curRect.value = {
    left = lerp(begin.left, end.left, t),
    top = lerp(begin.top, end.top, t),
    width = lerp(begin.width, end.width, t),
    height = lerp(begin.height, end.height, t),
  }
}

更多共享元素动画原理请查看 官方文档

手势搭配

打开图片之后,我们经常需要用到手势来操作图片,如缩放、移动、双击等等

我们上次学过的 手势系统 又派上用场啦

通过监听手势事件配合 worklet 函数即可在小程序实现图片预览效果

👇 下面演示缩放手势的处理,除了缩放之外,相册在手势处理上还有很多复杂的逻辑,包括惯性、边界逻辑判断等 点击查看更多相册相关的手势操作

// .wxml
// 绑定缩放手势
<scale-gesture-handler onGestureEvent="onScale">
  <view id="image">
    <image></image>
  </view>
</scale-gesture-handler>

let sharedValues = this.sharedValues ?? []

// .js
// 绑定缩放
this.applyAnimatedStyle('#image', () => {
  'worklet'
  // worklet 函数,sharedValues 变化时,函数会立即执行
  return {
    transform: `scale(${sharedValues[SCALE].value})`
  }
})

// 监听缩放
onScale(evt) {
  'worklet'
  
  // 连续的手势状态 && 双指放缩
  if (evt.state === GestureState.ACTIVE && evt.pointerCount === 2) {

	// 计算出当前真正的缩放值
	sharedValues[SCALE].value = evt.scale / sharedValues[TEMP_LAST_SCALE].value
	sharedValues[TEMP_LAST_SCALE].value = evt.scale
  }
}


最后,我们来看下小程序实现出来的相册跟原生相册的使用对比,在小程序也可以顺滑的实现类原生的效果啦~

目前,同程旅行 已经上线了共享元素结合手势的相册效果,mark这个 相册源码 直接接入到你的小程序吧~


10 回复

效果很酷。

开发者工具上可以,手机上看不见效果

不错,很棒的动效案例

AppID:wxb686573fd29a8e2c

未设置过滤违法、违规等不当信息内容的机制,已经设置好过滤机制并成功上线了,为什么还是申诉不通过?请平台管理人员重新进行审核

好棒呀!

同程这个看着好舒服,是加了物理回弹曲线了吗?

起飞,用起来

回到顶部