微信小程序——最炫乡村风,流传在父母圈里的动态相册集

市场下沉早已不是新词,可最近俺的麻麻总给我分享一个叫做心语相册的小程序,就是她把我的结婚照上传上去就产生类似幻灯片的动态效果,还带bgm,粗制滥造,满屏广告,可我又不得不做出相应的回应,不说他们的产品如何,只说这款小程序还是挺符合乡村场景的,今天就试着做个demo,回头也做款类似纯净版产品。
看效果
在这里插入图片描述
体验:
在这里插入图片描述
写这个demo的新路历程:
1、第一眼觉得乱糟糟挺复杂;
2、后来想想其实就是图片叠加、动画、音频播放这些内容;
3、做的时候发现卧槽,脑子里好多好多idea,但就是组不成一套模板,threejs都想使用,反正就是为了一个字;
4、不管了,写个最简单的看看效果吧
思路:
1、这种目前看都是按模板分的,所以首先要有模板概念,就是一套模板一套样式(样式包括wxss、wxml、也包括动效、甚至包括js代码),实现一个再说吧
2、音乐播放用InnerAudioContext或者背景音频都好,看需求了,只是要控制好生命周期(兼容好黑屏等异常情况)并做到与旋转动画同步,即音乐符号在旋转,音乐就要在播放,反之亦然
3、图片素材分为可更换区域与不可更换区域,整个布局采用position:absolute,并通过z-index控制好层级,不可更换区域采用png素材
4、动效:因为动画都采用循环播放,所以尽量使动画从哪里出发就要回到哪里
5、如果有特殊需求,比如下图情况,需自行将可更换图片裁剪后与png拼接,利用canvas即可
在这里插入图片描述
完整代码:
js

// miniprogram/pages/index/pages/photoAlbum/photoAlbum.js
var _animation; // 动画实体
var _animationIndex = 0; // 动画执行次数index(当前执行了多少次)
var _animationIntervalId = -1; // 动画定时任务id,通过setInterval来达到无限旋转,记录id,用于结束定时任务
const _ANIMATION_TIME = 100; // 动画播放一次的时长ms
const innerAudioContext = wx.createInnerAudioContext();
Page({

  /**
   * 页面的初始数据
   */
  data: {
    animation:{
      image1:"fadeOut",
      image2:"fadeIn"
    },
    imageSrc: ["cloud://normal-env-ta624598/meinv/000051.jpg","cloud://normal-en4598/meinv/000052.jpg"],
    systemInfo:{},
    menuButtonBoundingClientRect:{},
    animationImage:{},
    isRing: false, 
    audiolist: [
      {
        audiosrc: 'https://6e6f-normal-env-ta6pc-1300924598.2d8283a4cad0.mp3?sign=b4889da8c5373cad5279086359f27fcc&t=1583118236',
        audioImage: "cloud://norma51530,795833927&fm=26&gp=0.jpg", audioName: "上海滩",
      }
    ],
    isPlayAudio: false,
    audioSeek: 0,
    audioDuration: 0,
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var that = this
    wx.getSystemInfo({
      success: function(res) {
        that.setData({
          menuButtonBoundingClientRect: wx.getMenuButtonBoundingClientRect(),
          systemInfo:res
        })
      }
    })

  },
  controlMusic(){
    if(this.data.isRing){
      this.stopAnimationInterval()
    }else{
      this.startAnimationInterval()
    }
    this.playAudio()
  },
  /**
   * 实现image旋转动画,每次旋转 120*n度
   */
  rotateAni: function (n) {
    _animation.rotate(5 * (n)).step()
    this.setData({
      animationImage: _animation.export()
    })
  },

  /**
   * 开始旋转
   */
  startAnimationInterval: function () {
    var that = this;
    if (that.data.isRing) {
      return
    }
    that.setData({
      isRing: true
    })
    that.rotateAni(++_animationIndex); // 进行一次旋转
    _animationIntervalId = setInterval(function () {
      that.rotateAni(++_animationIndex);
    }, _ANIMATION_TIME); // 每间隔_ANIMATION_TIME进行一次旋转
  },

  /**
   * 停止旋转
   */
  stopAnimationInterval: function () {
    var that = this;
    if (!that.data.isRing) {
      return
    }
    that.setData({
      isRing: false
    })
    if (_animationIntervalId > 0) {
      clearInterval(_animationIntervalId);
      _animationIntervalId = 0;
    }
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    this.Initialization();
    this.loadaudio();

    _animation = wx.createAnimation({
      duration: _ANIMATION_TIME,
      timingFunction: 'linear', // "linear","ease","ease-in","ease-in-out","ease-out","step-start","step-end"
      delay: 0,
      transformOrigin: '50% 50% 0'
    })
    this.startAnimationInterval()
    this.playAudio()
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {
    if (this.data.isPlayAudio) {
      this.playAudio()
    }
    this.stopAnimationInterval()
    clearInterval(this.data.durationIntval)
  },
  //初始化播放器,获取duration
  Initialization() {
    var t = this;
    if (this.data.audiolist[0].audiosrc.length != 0) {
      //设置src
      innerAudioContext.src = this.data.audiolist[0].audiosrc;
      innerAudioContext.loop = true
      //运行一次
      innerAudioContext.play();
      innerAudioContext.pause();
      innerAudioContext.onCanplay(() => {
        //初始化duration
        innerAudioContext.duration
        setTimeout(function () {
          //延时获取音频真正的duration
          var duration = innerAudioContext.duration;
          t.setData({ 
            audioDuration: innerAudioContext.duration
            });
        }, 1000)
      })
    }
  },
  
  //播放、暂停按钮
  playAudio() {
    //获取播放状态和当前播放时间
    var isPlayAudio = this.data.isPlayAudio;
    var seek = this.data.audioSeek;
    innerAudioContext.pause();
    //更改播放状态
    this.setData({ isPlayAudio: !isPlayAudio })
    if (isPlayAudio) {
      //如果在播放则记录播放的时间seek,暂停
      this.setData({ audioSeek: innerAudioContext.currentTime });
    } else {
      //如果在暂停,获取播放时间并继续播放
      innerAudioContext.src = this.data.audiolist[0].audiosrc;
      if (innerAudioContext.duration != 0) {
        this.setData({ audioDuration: innerAudioContext.duration });
      }
      //跳转到指定时间播放
      innerAudioContext.seek(seek);
      innerAudioContext.play();
    }
  },
  loadaudio() {
    var that = this;
    //设置一个计步器
    this.data.durationIntval = setInterval(function () {
      //当歌曲在播放时执行
      if (that.data.isPlayAudio == true) {
        //获取歌曲的播放时间,进度百分比
        var seek = that.data.audioSeek;
        var duration = innerAudioContext.duration;
        var time = that.data.audioTime;
        time = parseInt(100 * seek / duration);
        //当歌曲在播放时,每隔一秒歌曲播放时间+1,并计算分钟数与秒数
        var min = parseInt((seek + 1) / 60);
        var sec = parseInt((seek + 1) % 60);
        //填充字符串,使3:1这种呈现出 03:01 的样式
        if (min.toString().length == 1) {
          min = `0${min}`;
        }
        if (sec.toString().length == 1) {
          sec = `0${sec}`;
        }
        var min1 = parseInt(duration / 60);
        var sec1 = parseInt(duration % 60);
        if (min1.toString().length == 1) {
          min1 = `0${min1}`;
        }
        if (sec1.toString().length == 1) {
          sec1 = `0${sec1}`;
        }
        //当进度条完成,停止播放,并重设播放时间和进度条
        if (time >= 100) {
          innerAudioContext.stop();
          that.setData({ audioSeek: 0, audioTime: 0, audioDuration: duration, isPlayAudio: false, showTime1: `00:00`, playSrc: './play-normal.png' });
          that.playAudio()
          return false;
        }
        //正常播放,更改进度信息,更改播放时间信息
        that.setData({ audioSeek: seek + 1, audioTime: time, audioDuration: duration, showTime1: `${min}:${sec}`, showTime2: `${min1}:${sec1}` });
      }
    }, 1000);
  },
  chooseImage(){
    var that = this
    wx.chooseImage({
      count: 2, // 默认9
      sizeType: ['compressed'], // 指定只能为压缩图,首先进行一次默认压缩
      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
      success: function(res) {
        var imageSrc = that.data.imageSrc
        for (var i = 0; i < res.tempFilePaths.length;i++){
          imageSrc[i] = res.tempFilePaths[i]
        }
        that.setData({
          imageSrc:imageSrc
        })
      },
    })
  },
})

wxml

<view class="music-bg" style="top:{{menuButtonBoundingClientRect.bottom+10}}px;right:20px"></view>
<image id="musicTip" class="music" src="./music.png" style="top:{{menuButtonBoundingClientRect.bottom+10}}px;right:20px" animation="{{animationImage}}" bindtap="controlMusic"></image> 
<view class="container">
  <image class="image1 {{animation.image1}}" src="{{imageSrc[0]}}" data-position="image1"></image>
  <image class="image1 {{animation.image2}}" src="{{imageSrc[1]}}" data-position="image2"></image>
  <image class="imageFrame" src="cloud://normal-env-ta6pc.6e6f-normal-env-ta6pc-1300924598/photoalbum/album1/kisspng-picture-frame-flower-rose-mood-frame-pictures-5a7a694429a400.2045124915179717801706.png"></image>
  <image class="imagePuzzle1 pulse" src="cloud://normal-env-ta6pc.6e6f-normal-env-ta6pc-1300924598/photoalbum/album1/kisspng-transparency-and-translucency-rose-red-transparent-bubble-effect-element-5a8ac8d4844b93.1305078815190448205419.png">
  </image>
  <view class="view-control">
    <button class="view-change" hover-class="view-change-hover" bindtap="chooseImage">换照片</button>
    <button class="view-change" hover-class="view-change-hover" style="margin-left:50rpx" open-type="share">分享给朋友</button>
  </view>
</view>

wxss

page{
  width: 100%;
  height: 100%;
}
.container{
  width: 100%;
  height: 100%;
  position: relative
}
.music{
  width: 24px;
  height: 24px;
  padding: 6px;
  position: fixed;
  z-index: 1001;
}
.music-bg{
  width: 32px;
  height: 32px;
  background: black;
  border-radius: 18px;
  opacity: 0.5;
  border: 2px solid white;
  position: fixed;
  z-index: 1000;
}
.view-control{
  width: 90%;
  display: flex;
  align-content: center;
  align-items: center;
  flex-direction: row;
  position: fixed;
  bottom: 32rpx;
  z-index: 1000;
}
.view-change{
  color: white;
  background: yellowgreen;
  border: 1rpx solid rgb(144, 192, 49);
}
.view-change-hover{
  color: white;
  background: rgb(141, 189, 47);
  border: 1rpx solid rgb(123, 163, 42)
}
.image1{
  width: 100%;
  height:100%;
  position: absolute;
  z-index: 10;
  /* filter: blur(3px) */
}
.imageFrame{
  width: 100%;
  height:100%;
  position: absolute;
  z-index: 30;
}

.imagePuzzle1{
  width: 200px;
  height:200px;
  position: absolute;
  z-index: 31;
}

@-webkit-keyframes pulse {
  from {
    margin-top: 0rpx;
    margin-left: 0px;
  }

  25% {
    margin-top: 300px;
    margin-left: 200px;
  }

  50% {
    margin-top: 700px;
    margin-left: 0px;
  }

  75% {
    margin-top: 400px;
    margin-left: -200px;
  }

  to {
    margin-top: 0px;
    margin-left: 0px;
  }
}

@keyframes pulse {
  from {
    margin-top: 0rpx;
    margin-left: 0px;
  }

  25% {
    margin-top: 300px;
    margin-left: 200px;
  }

  50% {
    margin-top: 700px;
    margin-left: 0px;
  }

  75% {
    margin-top: 400px;
    margin-left: -200px;
  }

  to {
    margin-top: 0px;
    margin-left: 0px;
  }
}

.pulse {
  -webkit-animation-name: pulse;
  animation-name: pulse;
  animation-duration: 20s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@-webkit-keyframes fadeIn {
  from {
    opacity: 1;
  }

  50% {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes fadeIn {
  from {
    opacity: 1;
  }

  50% {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

.fadeIn {
  -webkit-animation-name: fadeIn;
  animation-name: fadeIn;
  animation-duration: 8s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* animation-iteration-count: infinite; */
}
@-webkit-keyframes fadeOut {
  from {
    opacity: 0;
  }

  50% {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

@keyframes fadeOut {
  from {
    opacity: 0;
  }

  50% {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

.fadeOut {
  -webkit-animation-name: fadeOut;
  animation-name: fadeOut;
  animation-duration: 8s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}

@-webkit-keyframes rotateInDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -180deg);
    transform: rotate3d(0, 0, 1, -180deg);
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 1, 0);
    transform: translate3d(0, 1, 0);
  }
}

@keyframes rotateInDownLeft {
  from {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: rotate3d(0, 0, 1, -180deg);
    transform: rotate3d(0, 0, 1, -180deg);
  }

  to {
    -webkit-transform-origin: left bottom;
    transform-origin: left bottom;
    -webkit-transform: translate3d(0, 1, 0);
    transform: translate3d(0, 1, 0);
  }
}

.rotateInDownLeft {
  -webkit-animation-name: rotateInDownLeft;
  animation-name: rotateInDownLeft;
  animation-duration: 8s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玩烂小程序

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值