index.js 13 KB

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