用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 查看內容

2012湖人vs热火:最近很火的 倒放挑戰 - ReverseVoice (微信小程序版 前后端源碼) Ts Node Taro ... ...

湖人vs爵士季后赛 www.nbgeh.club Rolan 2019-11-29 00:21

項目地址:https://github.com/smackgg/reversevoice整個項目其實很簡單,從本人在抖音和 B 站看到火起來到最終小程序上線也就幾天的下班時間就搞定了,11月16日上線至今用戶量還是蠻多的(主要當時做的快此類 app 比 ...

項目地址: https://github.com/smackgg/reversevoice

整個項目其實很簡單,從本人在抖音和 B 站看到火起來到最終湖人vs爵士季后赛上線也就幾天的下班時間就搞定了,11月16日上線至今用戶量還是蠻多的(主要當時做的快此類 app 比較少),現在已經出現了大量的更簡約更好的倒放挑戰 app,本項目開源僅供大家學習~

擁抱 TypeScript ~

順便小聲吐槽一下 Taro 對 Ts 的支持還是不夠啊,希望大家多去給 Taro 提 dts 的 PR ~

體驗

小程序二維碼

挑戰分享海報 (這個海報暫時有問題,修復代碼因為資質問題還沒有提交審核)

功能介紹/實現原理

  • 功能及實現原理簡述
  1. 小程序端用戶錄音并保存本地
  2. 錄音后將錄音文件上傳至后端進行倒放處理,并返回處理后的音頻 url
  3. 小程序端下載 url 文件,提示用戶反轉成功,將數據做本地 map
  4. 用戶點擊分享,生成分享鏈接,并將該分享正放、倒放視頻均傳至后端保存至七牛云
  5. 同時新建分享 room 保存用戶信息,返回 roomId
  6. 用戶分享(海報分享 canvas 動態生成分享碼海報)
  7. 其它用戶參加挑戰,存儲原理同 4,只是增加將挑戰者信息了存入 room 的邏輯
  • 音頻倒放

使用 ffmpeg 進行音頻倒放,核心代碼:

// 詳見 ./server/src/controllers/file.ts => function reverseVoice
import ffmpegPath from '@ffmpeg-installer/ffmpeg'
import ffprobePath from '@ffprobe-installer/ffprobe'
import ffmpeg from 'fluent-ffmpeg'
ffmpeg.setFfprobePath(ffprobePath.path)
ffmpeg.setFfmpegPath(ffmpegPath.path)

ffmpeg(filepath)
    .format('mp4')
    // 反轉
    .outputOptions([
      '-vf reverse',
      '-af areverse',
      '-preset',
      'superfast',
      '-y',
    ])
    .on('progress', (progress) => {
      // send upload progress
      console.log('upload-file-progress', progress.percent)
    })
    .on('error', (err) => {
      console.log(`Ffmpeg has been killed${err.message}`)
    })
    .toFormat('mp3')
    // 保存
    .save(publicPath + saveFilePath)
    .on('end', () => {
      // 獲取音頻信息(時長等)
      ffmpeg.ffprobe(publicPath + saveFilePath, (err, metadata) => {
        console.log(metadata.format.duration)
      })
    })
  • 小程序錄音

小程序錄音使用官方 api,詳細邏輯見 ./wechatapp/pages/index/index.tsx

錄音

  • 海報生成

利用 canvas 動態合成分享海報 /wechatapp/pages/sharePoster
需要動態請求頁面小程序碼,涉及微信AccessToken鑒權等,詳見 /server/src/controllers/wechat.ts, 下面貼出部分核心代碼

// 畫圖
const draw = async () => {
  // 繪制之前 loading
  Taro.showLoading({
    title: '海報生成中...',
    mask: true,
  })
  // 獲取圖片信息
  const [productImgInfo, qrcodeImgInfo] = await Promise.all([
    this.getImageInfo(sharePoster), // 獲取主圖
    this.getQrImgInfo(), // 獲取二維碼圖片
  ])

  // product image 寬高
  const pW = CANVAS_WIDTH
  const pH = (pW / productImgInfo.width) * productImgInfo.height

  // canvas 高度
  let canvasHeight = pH

  const ctx = Taro.createCanvasContext('canvas', null)

  ctx.fillStyle = '#fff'
  ctx.fillRect(0, 0, CANVAS_WIDTH, canvasHeight)

  // 繪制背景圖片
  ctx.drawImage(sharePoster, 0, 0, pW, pH)

  // 繪制二維碼 (因為有角度,需要旋轉畫布,再旋轉回來)
  ctx.rotate(-Math.PI / 32)
  ctx.translate(-25 * ratio, 10 * ratio)
  ctx.drawImage(qrcodeImgInfo.path, QR_LEFT, QR_TOP, QR_WIDTH, QR_WIDTH)
  ctx.rotate(Math.PI / 32)
  this.setState({
    canvasStyle: {
      ...this.state.canvasStyle,
      height: canvasHeight,
    },
  })
  ctx.stroke()
  setTimeout(() => {
    Taro.hideLoading()
    ctx.draw()
  }, 500)
}
// 微信小程序每個頁面幾乎都需要配置分享的參數,并且需要動態更改分享參數
// 所以抽離 HOC 組件,方便頁面使用
import { ComponentClass } from 'react'

import Taro from '@tarojs/taro'
import { connect } from '@tarojs/redux';
import defaultShareImg from '@/assets/images/share.png'

type Options = {
  title?: string
  imageUrl?: string
  path?: string
}

const defalutOptions: Options = {
  title: '你能聽懂我說啥么?最近很火的反轉錄音來啦~',
  imageUrl: defaultShareImg,
  path: 'pages/index/index',
}

function withShare() {
  return function demoComponent(Component: ComponentClass) {
    @connect(({ user }) => ({
      userInfo: user.userInfo
    }))
    class WithShare extends Component {
      $shareOptions?: Options
      async componentWillMount() {
        Taro.showShareMenu({
          withShareTicket: true,
        })

        if (super.componentWillMount) {
          super.componentWillMount()
        }
      }

      // 點擊分享的那一刻會進行調用
      onShareAppMessage() {
        // const sharePath = `${path}&shareFromUser=${userInfo.shareId}`
        let options = defalutOptions
        if (this.$shareOptions) {
          options = {
            ...defalutOptions,
            ...this.$shareOptions,
          }
        }
        return options
      }

      render() {
        return super.render()
      }
    }

    return WithShare
  }
}

export default withShare

使用

@withShare()
class Room extends Component {
  /**
 * 指定config的類型聲明為: Taro.Config
 *
 * 由于 typescript 對于 object 類型推導只能推出 Key 的基本類型
 * 對于像 navigationBarTextStyle: 'black' 這樣的推導出的類型是 string
 * 提示和聲明 navigationBarTextStyle: 'black' | 'white' 類型沖突, 需要顯示聲明類型
 */
  config: Config = {
    navigationBarTitleText: '首頁',
  }

  $shareOptions = {
    title: '倒放挑戰!你能聽懂我倒立洗頭~',
    path: 'pages/index/index',
    imageUrl: '',
  }

  /**
    ....
  */
}
  • 微信用戶登錄流程

微信官方文檔登錄流程
具體實現可以去看源碼

項目運行 - 后端

準備

需要提前安裝:

開始

  • 克隆項目并進入后端目錄
cd server
  • 安裝依賴
npm install
  • 設置 mongoDB
# create the db directory
sudo mkdir -p /data/db
# give the db correct read/write permissions
sudo chmod 777 /data/db

# starting from macOS 10.15 even the admin cannot create directory at root
# so lets create the db diretory under the home directory.
mkdir -p ~/data/db
# user account has automatically read and write permissions for ~/data/db.
  • 啟動 mongoDB (Start your mongoDB server (you'll probably want another command prompt)
mongod

# on macOS 10.15 or above the db directory is under home directory
mongod --dbpath ~/data/db
  • 打包并運行項目
npm run build
npm start

項目運行 - 小程序端

準備

需要提前安裝:

開始

  • 克隆項目并進入小程序目錄
cd wechatapp
  • 安裝依賴
npm install
  • 新建 .env 文件
在 wechatapp/src/utils 目錄下克隆 env.example.ts 文件至同目錄命名為 .env.ts 文件
此文件兩個參數分別代表本地開發和線上部署的請求地址
  • 運行項目
npm run dev:weapp // development mode
或者 npm run build:weapp // production mode
  • 微信開發者工具
選擇導入項目,并選擇 wechatapp/dist 目錄
若本地開發,需要在開發者工具中設置開啟“不校驗合法域名“

License

MIT

分享至 : QQ空間
收藏
原作者: smackgg 來自: segmentfault
足彩混合过关的胜其它 广东十一选五360数据 广东十一选五走势图下载 赚钱宝DMZ华为 广西十一选五分析软件 欢乐捕鱼人赢话费 深海捕鱼免费破解推荐 秒速时时彩正规吗 排五玩法奖金规则 江西多乐彩彩彩乐乐 黑龙江11选5 日韩联赛投注技巧 北京赛车pk平台代理 国外视频网站上传赚钱吗 模拟器能用的赚钱软件下载 500元 倍投方案 稳赚