index.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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. async changeAvatar(e) {
  91. const {
  92. avatarUrl
  93. } = e.detail
  94. let res = await this.uploadFile(avatarUrl)
  95. this.setData({
  96. changeAvatarFlag: true,
  97. avatar: res
  98. })
  99. },
  100. play() {
  101. if (this.data.state == 'before') {
  102. this.innerAudioContext.src = this.data.configure.bgMusic; // 这里可以是录音的临时路径
  103. this.innerAudioContext.play();
  104. this.innerAudioContext.onEnded((res) => {
  105. console.log('音频播放完毕');
  106. this.setData({
  107. userAudioState: false
  108. })
  109. });
  110. this.setData({
  111. state: 'process'
  112. })
  113. let countDown = Number(this.data.configure.bgMusicLength)
  114. // 倒计时
  115. this.dsq = setInterval(item => {
  116. console.log('仍然倒计时呢');
  117. // 倒计时结束
  118. if (countDown == 1) {
  119. this.stopRecording()
  120. }
  121. if (countDown % 10 == 0) {
  122. this.setData({
  123. tens: --this.data.tens,
  124. bits: 9
  125. })
  126. } else {
  127. this.setData({
  128. bits: --this.data.bits
  129. })
  130. }
  131. --countDown
  132. }, 1000)
  133. const options = {
  134. sampleRate: 44100, //采样率
  135. numberOfChannels: 1, //录音通道数
  136. encodeBitRate: 192000, //编码码率
  137. format: 'mp3', //音频格式,有效值aac/mp3
  138. frameSize: 50 //指定帧大小,单位 KB
  139. };
  140. //开始录音,在开始录音回调中feed音频片
  141. recorderManager.start(options);
  142. //监听录音结束事件
  143. recorderManager.onStop((res) => {
  144. this.setData({
  145. tempFilePath: res.tempFilePath,
  146. });
  147. this.uploadAudio(res.tempFilePath)
  148. });
  149. } else {
  150. this.stopRecording()
  151. }
  152. },
  153. stopRecording() {
  154. clearInterval(this.dsq)
  155. this.innerAudioContext.stop();
  156. recorderManager.stop();
  157. this.setData({
  158. state: 'after',
  159. tens: this.data.configure.bgMusicLength[0],
  160. bits: this.data.configure.bgMusicLength[1]
  161. })
  162. },
  163. uploadAudio(recordSource) {
  164. this.setData({
  165. uploadState: true
  166. });
  167. const uploadTask = wx.uploadFile({
  168. url: 'https://reader-api.ai160.com//file/upload',
  169. filePath: recordSource,
  170. name: '朗读录音',
  171. header: {
  172. uid: wx.getStorageSync('uid')
  173. },
  174. success: (res) => {
  175. const formateRes = JSON.parse(res.data);
  176. let audioPath = formateRes.data;
  177. this.setData({
  178. audioPath
  179. })
  180. this.uploadActivity()
  181. },
  182. })
  183. },
  184. // 上传贺卡
  185. async uploadActivity() {
  186. this.createActivityImg('upload').then(async res => {
  187. let cardUrl = await this.uploadFile(res)
  188. let data = {
  189. audioPath: this.data.audioPath,
  190. // 生成贺卡图片地址
  191. cardUrl,
  192. toText: this.data.configure.toText,
  193. fromText: this.data.configure.fromText,
  194. templateId: this.data.id
  195. }
  196. console.log(data, 'data');
  197. let greetingCard = await saveActivity(data)
  198. console.log('贺卡生成', greetingCard);
  199. this.setDuration('endTime', greetingCard.duration)
  200. this.setData({
  201. greetingCard,
  202. uploadState: false
  203. })
  204. await actionRecord({
  205. action: 'NEW_YEAR_ACTIVITY_GENERATE_TEMPLATE',
  206. targetContent: this.data.id
  207. })
  208. })
  209. },
  210. playUserAudio() {
  211. if (!this.innerAudioContext) {
  212. this.innerAudioContext = wx.createInnerAudioContext();
  213. }
  214. if (this.data.userAudioState) {
  215. this.innerAudioContext.stop();
  216. this.setData({
  217. userAudioState: false
  218. })
  219. } else {
  220. this.innerAudioContext.src = this.data.greetingCard.audioPath;
  221. this.innerAudioContext.onTimeUpdate(() => {
  222. this.setDuration('playTime', this.innerAudioContext.currentTime)
  223. this.setData({
  224. vProgress: Math.ceil((Math.ceil(this.innerAudioContext.currentTime) / this.innerAudioContext.duration) * 100)
  225. })
  226. })
  227. this.innerAudioContext.play();
  228. this.setData({
  229. userAudioState: true
  230. })
  231. }
  232. },
  233. bindKeyInput(e) {
  234. if (e.currentTarget.dataset.type == 'from') {
  235. this.setData({
  236. fromWidth: e.detail.cursor * this.data.configure.fromFontSize,
  237. 'configure.fromText': e.detail.value
  238. })
  239. } else if (e.currentTarget.dataset.type == 'to') {
  240. this.setData({
  241. toWidth: e.detail.cursor * this.data.configure.toFontSize,
  242. 'configure.toText': e.detail.value
  243. })
  244. }
  245. },
  246. // 上传图片
  247. uploadFile(filePath) {
  248. return new Promise((resolve, reject) => {
  249. wx.uploadFile({
  250. url: 'https://reader-api.ai160.com/file/upload',
  251. filePath,
  252. name: '头像',
  253. header: {
  254. uid: wx.getStorageSync('uid')
  255. },
  256. success: (res) => {
  257. const result = JSON.parse(res.data).data;
  258. resolve(result)
  259. }
  260. })
  261. })
  262. },
  263. // 生成活动图片
  264. createActivityImg(createType = 'share') {
  265. return new Promise(async (resolve, reject) => {
  266. if (createType == 'share') {
  267. let title = await getShareText({
  268. cardReadId: this.data.greetingCard.id
  269. })
  270. resolve({
  271. title,
  272. path: `/pages/greeting/index?uid=${wx.getStorageSync('uid')}&cardId=${this.data.greetingCard.id}`,
  273. imageUrl: this.data.greetingCard.cardUrl
  274. })
  275. }
  276. let context = wx.createSelectorQuery();
  277. context
  278. .select('#share')
  279. .fields({
  280. node: true,
  281. size: true
  282. }).exec((res) => {
  283. const canvas = res[0].node;
  284. const ctx = canvas.getContext('2d');
  285. const dpr = wx.getSystemInfoSync().pixelRatio;
  286. canvas.width = res[0].width * dpr;
  287. canvas.height = res[0].height * dpr;
  288. ctx.scale(dpr, dpr);
  289. let avatar = canvas.createImage();
  290. avatar.src = this.data.avatar
  291. avatar.onload = () => {
  292. 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);
  293. let bgImg = canvas.createImage();
  294. bgImg.src = this.data.configure.bgImg
  295. bgImg.onload = () => {
  296. ctx.drawImage(bgImg, 0, 0, 375, 300);
  297. ctx.font = `${this.data.configure.fromFontSize/2}px PingFang`;
  298. ctx.fillText(this.data.configure.toText, this.data.configure.templateBase.toLeft / 2, this.data.configure.templateBase.toTop / 2 + this.data.configure.fromFontSize / 2)
  299. ctx.fillText(this.data.configure.fromText, this.data.configure.templateBase.fromLeft / 2, this.data.configure.templateBase.fromTop / 2 + this.data.configure.toFontSize / 2)
  300. console.log(this.data.configure.toText, this.data.configure.templateBase.toLeft / 2, this.data.configure.templateBase.toTop / 2);
  301. console.log(this.data.configure.fromText, this.data.configure.templateBase.fromLeft / 2, this.data.configure.templateBase.fromTop / 2);
  302. setTimeout(() => {
  303. wx.canvasToTempFilePath({
  304. canvas: canvas,
  305. success(res) {
  306. resolve(res.tempFilePath)
  307. },
  308. fail(res) {
  309. reject()
  310. }
  311. }, this)
  312. }, 1000)
  313. }
  314. }
  315. })
  316. })
  317. },
  318. onShareAppMessage({
  319. from,
  320. }) {
  321. if (from == 'button') {
  322. actionRecord({
  323. action: 'NEW_YEAR_ACTIVITY_SEND_BLESSING',
  324. })
  325. const promise = new Promise(resolve => {
  326. this.createActivityImg().then(res => {
  327. resolve(res)
  328. })
  329. })
  330. return {
  331. title: '请欣赏我的课文朗读作品,点赞+评论。',
  332. path: `/pages/index/index?&uid=${wx.getStorageSync('uid')}`,
  333. imageUrl: 'http://reader-wx.ai160.com/images/reader/v3/shareContent.png',
  334. promise
  335. }
  336. } else {
  337. return {
  338. title: '课文朗读,从未如此有趣。',
  339. path: `/pages/index/index?&uid=${wx.getStorageSync('uid')}`,
  340. imageUrl: 'http://reader-wx.ai160.com/images/reader/v3/shareContent.png'
  341. }
  342. }
  343. },
  344. // 设置时间文案
  345. setDuration(label, s) {
  346. let t = '';
  347. s = Math.ceil(s);
  348. if (s > -1) {
  349. let min = Math.floor(s / 60) % 60;
  350. let sec = s % 60;
  351. if (min < 10) {
  352. t += "0";
  353. }
  354. t += min + ":";
  355. if (sec < 10) {
  356. t += "0";
  357. }
  358. t += sec;
  359. }
  360. this.setData({
  361. [label]: t,
  362. })
  363. },
  364. onHide() {
  365. recorderManager.stop()
  366. this.innerAudioContext.stop()
  367. },
  368. onUnload() {
  369. recorderManager.stop()
  370. this.innerAudioContext.stop()
  371. }
  372. })