index.js 14 KB

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