index.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import {
  2. getActivityInfo,
  3. getShareText,
  4. saveActivity
  5. } from '~/api/activity'
  6. import {
  7. actionRecord
  8. } from '~/api/global'
  9. /*微信录音*/
  10. let recorderManager = wx.getRecorderManager();
  11. let dsq
  12. Page({
  13. /**
  14. * 页面的初始数据
  15. */
  16. data: {
  17. // 'before录制前, process 录制中,after录制后'
  18. state: 'before',
  19. tens: 3,
  20. bits: 0,
  21. configure: {},
  22. avatar: '',
  23. // 本地录音地址
  24. tempFilePath: '',
  25. // 线上录音地址
  26. audioPath: '',
  27. uploadState: false,
  28. // 此id保存模板使用
  29. id: '',
  30. greeting: [],
  31. greetingCard: null,
  32. userAudioState: false,
  33. // 当前音频播放时长
  34. playTime: '00:00',
  35. endTime: '00:00',
  36. vProgress: 0
  37. },
  38. async onLoad(options) {
  39. let configure = await getActivityInfo(options.id)
  40. wx.setNavigationBarTitle({
  41. title: configure.title,
  42. })
  43. this.setData({
  44. configure,
  45. avatar: configure.photoText,
  46. id: options.id,
  47. greeting: configure.greeting.split('\n'),
  48. tens: configure.bgMusicLength[0],
  49. bits: configure.bgMusicLength[1]
  50. })
  51. this.innerAudioContext = wx.createInnerAudioContext();
  52. // 录音授权
  53. wx.getSetting({
  54. success(res) {
  55. console.log(res);
  56. if (!res.authSetting['scope.record']) {
  57. wx.authorize({
  58. scope: 'scope.record',
  59. success() {
  60. // 用户已经同意小程序使用录音功能,后续调用接口不会弹窗询问
  61. }
  62. })
  63. }
  64. }
  65. })
  66. await actionRecord({
  67. action: 'NEW_YEAR_ACTIVITY_CHOOSE_TEMPLATE',
  68. targetContent: options.id
  69. })
  70. },
  71. showActionSheet() {
  72. wx.chooseMedia({
  73. count: 1,
  74. mediaType: ['image'],
  75. sourceType: ['album', 'camera'],
  76. camera: 'back',
  77. success: (res) => {
  78. this.cropper = this.selectComponent("#yeyouzi-cropper");
  79. this.cropper.init({
  80. imgPath: res.tempFiles[0].tempFilePath, //imgPath是需要裁剪图片的图片路径,只支持本地或临时路径
  81. success: (res) => {
  82. this.changeAvatar(res)
  83. },
  84. fail(error) {
  85. console.log(error) //有两种:cancel代表点击了叉,fail代表wx.canvasToTempFilePath生成图片失败
  86. }
  87. });
  88. }
  89. })
  90. },
  91. async changeAvatar(e) {
  92. let res = await this.uploadFile(e)
  93. this.setData({
  94. avatar: res
  95. })
  96. },
  97. play() {
  98. if (this.data.state == 'before') {
  99. this.innerAudioContext.src = this.data.configure.bgMusic; // 这里可以是录音的临时路径
  100. this.innerAudioContext.play();
  101. this.innerAudioContext.onEnded((res) => {
  102. console.log('音频播放完毕');
  103. this.setData({
  104. userAudioState: false
  105. })
  106. });
  107. this.setData({
  108. state: 'process'
  109. })
  110. let countDown = Number(this.data.configure.bgMusicLength)
  111. // 倒计时
  112. this.dsq = setInterval(item => {
  113. console.log('仍然倒计时呢');
  114. // 倒计时结束
  115. if (countDown == 1) {
  116. this.stopRecording()
  117. }
  118. if (countDown % 10 == 0) {
  119. this.setData({
  120. tens: --this.data.tens,
  121. bits: 9
  122. })
  123. } else {
  124. this.setData({
  125. bits: --this.data.bits
  126. })
  127. }
  128. --countDown
  129. }, 1000)
  130. const options = {
  131. sampleRate: 44100, //采样率
  132. numberOfChannels: 1, //录音通道数
  133. encodeBitRate: 192000, //编码码率
  134. format: 'mp3', //音频格式,有效值aac/mp3
  135. frameSize: 50 //指定帧大小,单位 KB
  136. };
  137. //开始录音,在开始录音回调中feed音频片
  138. recorderManager.start(options);
  139. //监听录音结束事件
  140. recorderManager.onStop((res) => {
  141. this.setData({
  142. tempFilePath: res.tempFilePath,
  143. });
  144. this.uploadAudio(res.tempFilePath)
  145. });
  146. } else {
  147. this.stopRecording()
  148. }
  149. },
  150. stopRecording() {
  151. clearInterval(this.dsq)
  152. this.innerAudioContext.stop();
  153. recorderManager.stop();
  154. this.setData({
  155. state: 'after',
  156. tens: this.data.configure.bgMusicLength[0],
  157. bits: this.data.configure.bgMusicLength[1]
  158. })
  159. },
  160. uploadAudio(recordSource) {
  161. this.setData({
  162. uploadState: true
  163. });
  164. const uploadTask = wx.uploadFile({
  165. url: 'https://reader-api.ai160.com//file/upload',
  166. filePath: recordSource,
  167. name: '朗读录音',
  168. header: {
  169. uid: wx.getStorageSync('uid')
  170. },
  171. success: (res) => {
  172. const formateRes = JSON.parse(res.data);
  173. let audioPath = formateRes.data;
  174. this.setData({
  175. audioPath
  176. })
  177. this.uploadActivity()
  178. },
  179. })
  180. },
  181. // 上传贺卡
  182. async uploadActivity() {
  183. this.createActivityImg('upload').then(async res => {
  184. let cardUrl = await this.uploadFile(res)
  185. let data = {
  186. audioPath: this.data.audioPath,
  187. // 生成贺卡图片地址
  188. cardUrl,
  189. toText: this.data.configure.toText,
  190. fromText: this.data.configure.fromText,
  191. templateId: this.data.id
  192. }
  193. console.log(data, 'data');
  194. let greetingCard = await saveActivity(data)
  195. console.log('贺卡生成', greetingCard);
  196. this.setDuration('endTime', greetingCard.duration)
  197. this.setData({
  198. greetingCard,
  199. uploadState: false
  200. })
  201. await actionRecord({
  202. action: 'NEW_YEAR_ACTIVITY_GENERATE_TEMPLATE',
  203. targetContent: this.data.id
  204. })
  205. })
  206. },
  207. playUserAudio() {
  208. if (!this.innerAudioContext) {
  209. this.innerAudioContext = wx.createInnerAudioContext();
  210. }
  211. if (this.data.userAudioState) {
  212. this.innerAudioContext.stop();
  213. this.setData({
  214. userAudioState: false
  215. })
  216. } else {
  217. this.innerAudioContext.src = this.data.greetingCard.audioPath;
  218. this.innerAudioContext.onTimeUpdate(() => {
  219. this.setDuration('playTime', this.innerAudioContext.currentTime)
  220. this.setData({
  221. vProgress: Math.ceil((Math.ceil(this.innerAudioContext.currentTime) / this.innerAudioContext.duration) * 100)
  222. })
  223. })
  224. this.innerAudioContext.play();
  225. this.setData({
  226. userAudioState: true
  227. })
  228. }
  229. },
  230. bindKeyInput(e) {
  231. if (e.currentTarget.dataset.type == 'from') {
  232. this.setData({
  233. 'configure.fromText': e.detail.value
  234. })
  235. } else if (e.currentTarget.dataset.type == 'to') {
  236. this.setData({
  237. 'configure.toText': e.detail.value
  238. })
  239. }
  240. },
  241. // 上传图片
  242. uploadFile(filePath) {
  243. return new Promise((resolve, reject) => {
  244. wx.uploadFile({
  245. url: 'https://reader-api.ai160.com/file/upload',
  246. filePath,
  247. name: '头像',
  248. header: {
  249. uid: wx.getStorageSync('uid')
  250. },
  251. success: (res) => {
  252. const result = JSON.parse(res.data).data;
  253. resolve(result)
  254. }
  255. })
  256. })
  257. },
  258. // 生成活动图片
  259. createActivityImg(createType = 'share') {
  260. return new Promise(async (resolve, reject) => {
  261. if (createType == 'share') {
  262. let title = await getShareText({
  263. cardReadId: this.data.greetingCard.id
  264. })
  265. resolve({
  266. title,
  267. path: `/pages/greeting/index?uid=${wx.getStorageSync('uid')}&cardId=${this.data.greetingCard.id}`,
  268. imageUrl: this.data.greetingCard.cardUrl
  269. })
  270. }
  271. let context = wx.createSelectorQuery();
  272. context
  273. .select('#share')
  274. .fields({
  275. node: true,
  276. size: true
  277. }).exec((res) => {
  278. const canvas = res[0].node;
  279. const ctx = canvas.getContext('2d');
  280. const dpr = wx.getSystemInfoSync().pixelRatio;
  281. canvas.width = res[0].width * dpr;
  282. canvas.height = res[0].height * dpr;
  283. ctx.scale(dpr, dpr);
  284. let avatar = canvas.createImage();
  285. avatar.src = this.data.avatar
  286. avatar.onload = () => {
  287. ctx.drawImage(avatar, this.data.configure.templateBase.photoLeft / 2, this.data.configure.templateBase.photoTop / 2, this.data.configure.templateBase.photoWidth / 2, this.data.configure.templateBase.photoHeight / 2);
  288. let bgImg = canvas.createImage();
  289. bgImg.src = this.data.configure.bgImg
  290. bgImg.onload = () => {
  291. ctx.drawImage(bgImg, 0, 0, 375, 300);
  292. ctx.font = `${this.data.configure.fromFontSize/2}px PingFang`;
  293. ctx.fillStyle = `${this.data.configure.toColor}`;
  294. let toLeft = 0
  295. if (this.data.configure.toTextAlign == 'left') {
  296. toLeft = this.data.configure.templateBase.toLeft / 2
  297. } else if (this.data.configure.toTextAlign == 'right') {
  298. toLeft = this.data.configure.templateBase.toLeft / 2 + (this.data.configure.templateBase.toWidth / 2 - ctx.measureText(this.data.configure.toText).width)
  299. } else if (this.data.configure.toTextAlign == 'center') {
  300. toLeft = this.data.configure.templateBase.toLeft / 2 + ((this.data.configure.templateBase.toWidth / 2 - ctx.measureText(this.data.configure.toText).width) / 2)
  301. }
  302. ctx.fillText(this.data.configure.toText, toLeft, this.data.configure.templateBase.toTop / 2 + this.data.configure.fromFontSize / 2)
  303. ctx.fillStyle = `${this.data.configure.fromColor}`;
  304. let fromLeft = 0
  305. if (this.data.configure.fromTextAlign == 'left') {
  306. fromLeft = this.data.configure.templateBase.fromLeft / 2
  307. } else if (this.data.configure.fromTextAlign == 'right') {
  308. fromLeft = this.data.configure.templateBase.fromLeft / 2 + (this.data.configure.templateBase.fromWidth / 2 - ctx.measureText(this.data.configure.fromText).width)
  309. } else if (this.data.configure.fromTextAlign == 'center') {
  310. fromLeft = this.data.configure.templateBase.fromLeft / 2 + ((this.data.configure.templateBase.fromWidth / 2 - ctx.measureText(this.data.configure.fromText).width) / 2)
  311. }
  312. ctx.fillText(this.data.configure.fromText, fromLeft, this.data.configure.templateBase.fromTop / 2 + this.data.configure.toFontSize / 2)
  313. setTimeout(() => {
  314. wx.canvasToTempFilePath({
  315. canvas: canvas,
  316. success(res) {
  317. resolve(res.tempFilePath)
  318. },
  319. fail(res) {
  320. reject()
  321. }
  322. }, this)
  323. }, 1000)
  324. }
  325. }
  326. })
  327. })
  328. },
  329. onShareAppMessage({
  330. from,
  331. }) {
  332. if (from == 'button') {
  333. actionRecord({
  334. action: 'NEW_YEAR_ACTIVITY_SEND_BLESSING',
  335. })
  336. const promise = new Promise(resolve => {
  337. this.createActivityImg().then(res => {
  338. resolve(res)
  339. })
  340. })
  341. return {
  342. title: '请欣赏我的课文朗读作品,点赞+评论。',
  343. path: `/pages/index/index?uid=${wx.getStorageSync('uid')}`,
  344. imageUrl: 'http://reader-wx.ai160.com/images/reader/v3/shareContent.png',
  345. promise
  346. }
  347. } else {
  348. return {
  349. title: '课文朗读,从未如此有趣。',
  350. path: `/pages/index/index?uid=${wx.getStorageSync('uid')}`,
  351. imageUrl: 'http://reader-wx.ai160.com/images/reader/v3/shareContent.png'
  352. }
  353. }
  354. },
  355. // 设置时间文案
  356. setDuration(label, s) {
  357. let t = '';
  358. s = Math.ceil(s);
  359. if (s > -1) {
  360. let min = Math.floor(s / 60) % 60;
  361. let sec = s % 60;
  362. if (min < 10) {
  363. t += "0";
  364. }
  365. t += min + ":";
  366. if (sec < 10) {
  367. t += "0";
  368. }
  369. t += sec;
  370. }
  371. this.setData({
  372. [label]: t,
  373. })
  374. },
  375. onHide() {
  376. recorderManager.stop()
  377. this.innerAudioContext.stop()
  378. },
  379. onUnload() {
  380. recorderManager.stop()
  381. this.innerAudioContext.stop()
  382. }
  383. })