敲鼓动效组件

133 阅读4分钟
  <view class="page">
    <view class="drum_bg">
      <view v-if="myMoonCake" class="moon_cake">我的月饼:{{ myMoonCake }}</view>
      <view class="drum_countdown" v-if="startDrum">
        {{ drumTimer }}
      </view>
      <view class="drum_number" :class="{ animation: showAnimation }" v-if="drumCount > 0">连击 X{{ drumCount }}</view>
      <view>
        <image :src="`${elec_ossImgUrl}rabbit_left.png`" mode="widthFix" class="rabbit_left" />
        <image :src="`${elec_ossImgUrl}rabbit_right.png`" mode="widthFix" class="rabbit_right" />
      </view>

      <view @click="changeMusic">
        <view class="music_bg" v-if="music"></view>
        <image :src="`${elec_ossImgUrl}drum_turnon.png`" mode="widthFix" v-if="music" class="music" :class="music ? 'rotate' : ''" />
        <image :src="`${elec_ossImgUrl}drum_turnoff.png`" mode="widthFix" v-else class="music" />
      </view>
      <view>
        <view class="drum_ready" :class="{ readyAnimation: showDrumReady }" v-if="startReady">{{ readyTime }}
        </view>
        <image :src="`${elec_ossImgUrl}drum_surface.png`" mode="widthFix" class="surface" @click="handleClick" />
        <view class="ripple_container">
          <view class="circle" :class="{ expand: showAnimation }"></view>
          <view class="circle1" :class="{ expand: showAnimation }"></view>
          <view class="circle2" :class="{ expand: showAnimation }"></view>
        </view>
      </view>
      <view v-if="drumUses" class="button_container">
        <view class="drum_times">
          <view class="times_num">{{ drumUses }}</view>
          <view class="times_text">剩余敲击机会</view>
        </view>
        <view class="drum_start button-top" @click="handleStart">
          <view class="start_text">{{ startDrum || startReady ? `敲击鼓面` : `开始敲击` }}</view>
        </view>
        <view class="drum_obtain" @click="getDrumUses">
          <slot name="getMoreChance"></slot>
          <view class="obtain_text">获取机会</view>
        </view>
      </view>
      <!-- 增加插槽 -->
      <slot></slot>
    </view>
  </view>
</template>

<script>
const { ossImgUrl } = getApp().globalData; //实例化app.js中的变量和常量

export default {
  props: {
    // 敲鼓时间
    drummingCountdown: {
      type: String,
      default: () => ''
    },
    // 敲鼓使用机会次数
    drumUses: {
      type: String,
      default: () => ''
    },
    // 我的月饼
    myMoonCake: {
      type: String,
      default: () => ''
    },
    bgMusic: {
      type: String,
      default: () => ''
    }
  },
  inject: ['getIsActionDrum'],
  data() {
    return {
      drumCount: 0,//打击次数
      drumTimer: null,
      readyTime: 3,//准备时间
      showAnimation: false,
      showDrumReady: false,
      startDrum: false,
      startReady: false,
      elec_ossImgUrl: `${ossImgUrl}electricalMall/`,
      music: true,
      backgroundMusic: null, // 背景音乐实例
      drumSound: null, // 鼓点音效实例
      userAgent: '',
      DrumRestrict: false,//重置打击音效时的限制
    };
  },
  computed: {
    isActionDrum() {
      let flag = false;
      if (this.getIsActionDrum) {
        flag = this.getIsActionDrum(); // getter
      } else {
        flag = false;
      }
      return flag;
    },
  },
  watch: {
    drummingCountdown(newVal) {
      if (newVal > 0 && !this.startDrum) {
        this.drumTimer = newVal;
      }
    }
  },
  mounted() {
    // this.audioConfig()
    // this.drumTimer = this.drummingCountdown
    const unWatch = this.$watch(() => this.bgMusic, (newVal) => {
      if (newVal) {
        this.audioConfig();
        unWatch();
      }
    }, { immediate: true });
  },
  onHide() {
    this.backgroundMusic.stop();
    this.drumSound.stop();
    this.music = false
  },
  beforeDestroy() {
    this.backgroundMusic.stop();
    this.drumSound.stop();
    this.music = false
  },
  methods: {
    audioConfig() {
      // 创建背景音乐实例
      用来创建并返回内部 audio 上下文 `innerAudioContext` 对象。APP、H5、微信小程序都支持此方法。
      
      this.backgroundMusic = uni.createInnerAudioContext();
      this.backgroundMusic.src = this.bgMusic; // 背景音乐路径
      this.backgroundMusic.volume = 0.5; // 设置背景音乐循环音量
      this.backgroundMusic.loop = true; // 设置背景音乐循环播放

      // #ifdef H5
      const userAgent = navigator.appVersion;
      let website = ['Chrome', 'Firefox', 'Safari', 'Edge', 'Opera', 'Chromium']
      let noPlay = false
      for (let i = 0; i < website.length; i++) {
        const element = website[i];
        noPlay = userAgent.includes(element);
        if (noPlay) {
          break
        }
      }
      if (noPlay) {
        this.music = false
        this.$forceUpdate()
      } else {
        this.backgroundMusic.play();
      }
      // #endif
      // #ifndef H5
      this.backgroundMusic.play();
      // #endif
      // 创建鼓点音效实例
      this.drumSound = uni.createInnerAudioContext();
      this.drumSound.src = `${this.elec_ossImgUrl}drum.mp3`; // 鼓点音效路径
      this.drumSound.volume = 1;
    },
    changeMusic() {
      this.music = !this.music;
      if (this.music) {
        this.backgroundMusic.play();
      } else {
        this.backgroundMusic.stop();
      }
    },
    addDrumRate() {
      let that = this
      if (!that.DrumRestrict) {
        that.drumSound.stop();
        that.DrumRestrict = true
        that.drumSound.seek(0);
        that.drumSound.play();
        setTimeout(() => {
          that.DrumRestrict = false;
        }, 200);
      }
    },

    handleClick() {
      let that = this
      // 点击敲打开始鼓点动画和音效
      that.showAnimation = true;
      setTimeout(() => {
        that.showAnimation = false;
      }, 200);
      that.addDrumRate()
      // end
      // 开始敲击之后开始计数
      if (that.startDrum) {
        that.drumCount++;
      }
    },
    handleStart() {
      if (this.startDrum || this.startReady) {
        return;
      }
      this.$emit('drumStart');
      if (this.drumUses > 0) {
        if (!this.startDrum) {
          if (this.drummingCountdown) { this.drumTimer = this.drummingCountdown; }
          // 开始敲击
          this.drumCount = 0
          this.readyTime = 3
          this.readyCountdown()
        }
      } else {
        uni.showToast({ title: '次数已用完,快去完成任务获取更多机会吧!', icon: 'none' })
        this.$emit('drumFail');
      }
    },

    // 准备倒计时
    readyCountdown() {
      this.startReady = true
      this.showDrumReady = true
      setTimeout(() => {
        this.showDrumReady = false;
      }, 200);
      let countdown = setInterval(() => {
        this.showDrumReady = true
        setTimeout(() => {
          this.showDrumReady = false;
        }, 200);
        this.readyTime--;
        if (this.readyTime <= 0) {
          clearInterval(countdown);
          this.startTiming()
        }
      }, 1000);
    },
    // 开始计数
    startTiming() {
      if (this.drumTimer > 0) {
        this.startReady = false
        this.startDrum = true;
        this.startCountdown();
      }
      setTimeout(() => {
        this.startDrum = false;
        this.showDrumReady = false
      }, this.drumTimer * 1000);
    },
    // 打击倒计时
    startCountdown() {
      let countdown = setInterval(() => {
        this.drumTimer--;
        if (this.drumTimer <= 0) {
          this.startDrum = false;
          this.$emit('drumCount', this.drumCount)
          clearInterval(countdown);
        }
      }, 1000);
    },
    // 获取使用机会
    getDrumUses() {
      // 如果正在执行中
      if (this.isActionDrum) return;
      this.$emit('getDrumUses')
    }
  }
};
</script>

<style>
.page {
  position: relative;
  touch-action: manipulation;
}

img {
  -webkit-touch-callout: none;
  /* 禁止长按图片弹出菜单 */
  -webkit-user-select: none;
  /* 禁止选择图片 */
  pointer-events: none;
  /* 禁止图片点击事件 */
}

.drum_bg {
  width: 750rpx;
  height: 1080rpx;
  background-image: url('https://tineco-gngw.oss-accelerate.aliyuncs.com/electricalMall/mid-autumn_bg.png');
  background-size: 100%;
  position: relative;
}

.moon_cake {
  width: 200rpx;
  height: 40rpx;
  font-size: 28rpx;
  font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  font-weight: 600;
  color: #df3d2c;
  line-height: 40rpx;
  margin: 730rpx 0 0 280rpx;
  text-align: center;
  position: absolute;
}

.music {
  width: 50rpx;
  height: 50rpx;
  margin: 30rpx 26rpx;
  z-index: 8;
  position: absolute;
}

.music_bg {
  width: 50rpx;
  height: 50rpx;
  margin: 30rpx 26rpx;
  background-color: #be7fce;
  border: 5rpx solid #be7fce;
  border-radius: 50%;
  animation: musicRotate 2s linear infinite;
  position: absolute;
  z-index: 1;
}

@keyframes musicRotate {
  0% {
    transform: scale(1.2);
    opacity: 0.8;
  }

  50% {
    transform: scale(1.5);
    opacity: 0.5;
  }

  100% {
    transform: scale(1.8);
    opacity: 0.2;
  }
}

.rotate {
  animation: rotate 3s linear infinite;
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.surface {
  width: 380rpx;
  height: 370rpx;
  margin: 294rpx 0 0 164rpx;
  pointer-events: auto;
}

.drum_ready {
  width: 200rpx;
  font-size: 100rpx;
  font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  font-weight: normal;
  color: #f84b31;
  text-shadow: 0px 4px 8px #cc9a06;
  z-index: 2;
  position: absolute;
  margin: 400rpx 0 0 260rpx;
  text-align: center;
  opacity: 1;
  transform: scale(1);
  transition: all 0.3s;
}

.drum_ready.readyAnimation {
  opacity: 1;
  transform: scale(2);
}

.button_container {
  display: flex;
  justify-content: center;
  align-items: top;
  position: absolute;
  bottom: 50rpx;
  left: 34rpx;
}

.button-top {
  margin: 16rpx 20rpx;
}

.drum_times {
  width: 164rpx;
  height: 122rpx;
  background-image: url('https://tineco-gngw.oss-accelerate.aliyuncs.com/electricalMall/drum_times.png');
  background-size: 100%;
  position: relative;
}

.times_num {
  font-size: 36rpx;
  font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  font-weight: 500;
  color: #df3d2c;
  line-height: 54rpx;
  margin-top: 22rpx;
  text-align: center;
}

.times_num:after {
  content: '次';
  font-size: 28rpx;
  font-family: SourceHanSansSC-Regular, SourceHanSansSC;
  font-weight: 500;
}

.times_text {
  width: 164rpx;
  font-size: 24rpx;
  font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  font-weight: 500;
  color: #feeeca;
  line-height: 36rpx;
  margin-top: 6rpx;
  margin-left: 12rpx;
  position: absolute;
}

.drum_start {
  width: 316rpx;
  height: 92rpx;
  background-image: url('https://tineco-gngw.oss-accelerate.aliyuncs.com/electricalMall/drum_start.png');
  background-size: 100%;
}

.start_text {
  width: 316rpx;
  height: 92rpx;
  font-size: 43rpx;
  font-family: SourceHanSansSC-Bold, SourceHanSansSC;
  font-weight: bold;
  color: #feeeca;
  line-height: 92rpx;
  text-align: center;
}

.drum_obtain {
  width: 156rpx;
  height: 156rpx;
  background-image: url('https://tineco-gngw.oss-accelerate.aliyuncs.com/electricalMall/drum_obtain.png');
  background-size: 100%;
  position: relative;
}

.obtain_text {
  width: 56rpx;
  font-size: 28rpx;
  font-family: SourceHanSansSC-Medium, SourceHanSansSC;
  font-weight: 500;
  color: #feeeca;
  margin-left: 52rpx;
  line-height: 32rpx;
  margin-top: 32rpx;
}

.rabbit_left {
  width: 257rpx;
  height: 290rpx;
  position: absolute;
  margin: 462rpx 0 0 56rpx;
  z-index: 2;
  pointer-events: none;
}

.rabbit_right {
  width: 287rpx;
  height: 355rpx;
  position: absolute;
  margin: 450rpx 0 0 436rpx;
  z-index: 2;
  pointer-events: none;
}

.drum_countdown {
  width: 90rpx;
  height: 91rpx;
  background-image: url('https://tineco-gngw.oss-accelerate.aliyuncs.com/electricalMall/drum_countdown.png');
  background-size: 100%;
  margin: 244rpx 0 0 16rpx;
  position: absolute;
  font-size: 36rpx;
  font-family: PingFangSC-Medium, PingFang SC;
  font-weight: 600;
  color: #fffaa5;
  line-height: 90rpx;
  text-align: center;
}

.drum_countdown:after {
  content: 's';
}
.drum_number {
  width: 750rpx;
  font-size: 48rpx;
  font-family: SourceHanSansSC-Bold, SourceHanSansSC;
  font-weight: bold;
  color: #fffaa5;
  line-height: 57rpx;
  text-shadow: 0px 4px 8px #b353cf;
  position: absolute;
  margin: 220rpx 0;
  text-align: center;
  opacity: 1;
  transform: scale(1);
  transition: all 0.2s;
}
//次数跳动效果
.drum_number.animation {
  opacity: 1;
  transform: scale(1.5);
}

.ripple_container {
  position: absolute;
  z-index: 1;
  top: 470rpx;
  left: 360rpx;
  pointer-events: none;
}

.circle {
  width: 10rpx;
  height: 10rpx;
  background-color: #ffe496;
  border: 5rpx solid #ffeed7;
  border-radius: 50%;
  overflow: hidden;
  transition: all 0.2s;
  position: absolute;
  z-index: 1;
}

.circle1 {
  width: 10rpx;
  height: 10rpx;
  background-color: #ffe496;
  border: 5rpx solid #ffe496;
  border-radius: 50%;
  overflow: hidden;
  transition: all 0.2s;
  position: absolute;
}

.circle2 {
  width: 10rpx;
  height: 10rpx;
  background-color: #ffe496;
  border: 5rpx solid #ffe496;
  border-radius: 50%;
  overflow: hidden;
  transition: all 0.2s;
  position: absolute;
}
//波纹效果 根据缩放实现
.circle.expand {
  transform: scale(20);
  opacity: 0;
}

.circle1.expand {
  transform: scale(40);
  opacity: 0;
}

.circle2.expand {
  transform: scale(60);
  opacity: 0;
}
</style>

动效演示地址

hepengwei.cn/#/canvas/pa…

源码地址

github.com/hepengwei/v…