Project address: https://github.com/smackgg/reversevoice
In fact, the whole project is very simple. It's only a few days after I saw the fire in tremolo and station B until the app finally went online. Since it went online on November 16, there are still a lot of users (mainly the fast apps made at that time are relatively few). Now there are a lot of simpler and better flip challenge apps. The open source of this project is only for you to learn~
Embrace TypeScript~
By the way, Taro's support for Ts is not enough. I hope you can go to Taro for dts's PR.
experience
Applet QR code
Challenge sharing Poster (there is a problem with this poster for the time being, the fix code has not been submitted for review because of the qualification problem)
Function introduction / implementation principle
- Function and realization principle
- Applet end users record and save local
- After recording, upload the recording file to the back end for playback, and return the processed audio url
- Download the url file on the applet side, prompt the user to reverse successfully, and make the data as a local map
- The user clicks share to generate a share link, and sends the video of the share in front and back to the back end to be saved in qiniu cloud
- At the same time, create a new sharing room to save the user information and return the roomId
- User sharing (poster sharing canvas dynamically generate sharing code poster)
- Other users take part in the challenge. The storage principle is the same as that of 4, but the logic of storing Challenger information into room is added
- Audio upside down
Use ffmpeg for audio playback, core code:
// See. / server / SRC / Controllers / file. TS = > function reversevoice for details 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') // Reversal .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') // Preservation .save(publicPath + saveFilePath) .on('end', () => { // Get audio information (duration, etc.) ffmpeg.ffprobe(publicPath + saveFilePath, (err, metadata) => { console.log(metadata.format.duration) }) })
- Applet recording
The official api is used for small program recording. For details, see ./wechatapp/pages/index/index.tsx
- Poster generation
Using canvas to dynamically synthesize and share posters /wechatapp/pages/sharePoster
Dynamic request page applet code is required, involving wechat AccessToken authentication, etc. for details, see /server/src/controllers/wechat.ts , some core codes are pasted below
// Drawing const draw = async () => { // loading before drawing Taro.showLoading({ title: 'Poster generation in progress...', mask: true, }) // Get picture information const [productImgInfo, qrcodeImgInfo] = await Promise.all([ this.getImageInfo(sharePoster), // Get master map this.getQrImgInfo(), // Get QR code picture ]) // product image width and height const pW = CANVAS_WIDTH const pH = (pW / productImgInfo.width) * productImgInfo.height // canvas height let canvasHeight = pH const ctx = Taro.createCanvasContext('canvas', null) ctx.fillStyle = '#fff' ctx.fillRect(0, 0, CANVAS_WIDTH, canvasHeight) // Draw background picture ctx.drawImage(sharePoster, 0, 0, pW, pH) // Draw the QR code (because there is an angle, you need to rotate the canvas, and then rotate it back) 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) }
- Wechat sharing HOC function ./wechatapp/components/@withShare
// Almost every page of wechat applet needs to be configured with shared parameters, and the shared parameters need to be changed dynamically // Therefore, it is convenient to use the page by taking out the HOC component 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: 'Can you understand me? Recently, the hot reverse recording is coming~', 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() } } // It will be called at the moment of clicking share 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 { /** * The type of the specified config is declared as: Taro.Config * * Because typescript can only deduce the basic type of Key for object type derivation * For a derived type like navigationBarTextStyle: 'black', the type is string * Prompt and declaration navigationBarTextStyle: 'black' | 'white' type conflict, declaration type needs to be displayed */ config: Config = { navigationBarTitleText: 'home page', } $shareOptions = { title: 'Rewind the challenge! You can understand my handstand Shampoo~', path: 'pages/index/index', imageUrl: '', } /** .... */ }
- Wechat user login process
Wechat official document login process
See the source code for specific implementation
Project run - back end
Get ready
Need to be installed in advance:
start
- Clone the project and go to the back-end directory
cd server
- Installation dependency
npm install
- Setting up 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.
- 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
- Package and run the project
npm run build npm start
Project running - applet side
Get ready
Need to be installed in advance:
- Install Wechat developer tools
start
- Clone the project and enter the applet directory
cd wechatapp
- Installation dependency
npm install
- New. env file
In the wechatapp/src/utils directory, clone the env.example.ts file to the same directory as the. env.ts file The two parameters of this file represent the request address of local development and online deployment respectively
- Operation item
npm run dev:weapp // development mode //Or npm run build:weapp // production mode
- Wechat developer tools
Select the import project and select the wechatapp/dist directory If you want to develop locally, you need to set "do not verify legal domain name" in the developer tool“
License
MIT