index.js 14 KB

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