captureFollow.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import getParams from './getParams.js';
  2. captureFollow = function () {
  3. //private menbers
  4. const dataUrl = "./json/kolData.json";
  5. const kolMan = ['Yang', 'Hao', 'Drangadrang', 'Cookingdairy', 'Ku', 'Betty', 'Ralf', 'Chefchouchou', 'Albee', 'commute_Chang', 'commute_Li', 'commute_He', 'ordinary']
  6. let kolName;
  7. let recipeData;
  8. let voiceData;
  9. let $ul = $("<ul></ul>");
  10. let $captureContainer = $(".recipe__list");
  11. let $ingredientContainer = $(".recipe__basket");
  12. const audio = document.querySelector('audio');
  13. let captureData = []; //
  14. let lineNo = 0; // 當前字幕
  15. let preLine = 0; // 當播放6行後開始滾動歌詞
  16. let lineHeight = 30; // 每次滾動的距離
  17. let preLineNo = null; //上一個當前字幕
  18. let prehighLine = null; // 上一個highlight的字幕
  19. let ingredientW = (95+15); //食材圖片寬度加margin-left
  20. //private methods
  21. function init() {
  22. console.log('captureFollow is loaded.');
  23. // 取得是哪位Kol
  24. if (getParams('kol') != null && kolMan.includes(getParams('kol'))) {
  25. kolName = getParams('kol')
  26. } else {
  27. kolName = 'Ralf' // 預設kol;
  28. }
  29. // console.log(kolName)
  30. fetch(dataUrl)
  31. .then(res=>res.json())
  32. .then(data=>{
  33. let getRecipe = data.filter(item=>item.kol.id == kolName
  34. );
  35. recipeData = getRecipe[0];
  36. console.log(recipeData)
  37. $("audio").attr("src","./audio/"+recipeData.voice.source)
  38. voiceData=recipeData.voice.subtitle;
  39. // console.log(voiceData)
  40. showLyric();
  41. audioEvent();
  42. });
  43. }
  44. function audioEvent(){
  45. // 當 currentTime 更新時會觸發 timeupdate 事件
  46. audio.addEventListener("timeupdate",function(){
  47. let curT = audio.currentTime;
  48. // console.log(curT);
  49. if(curT>0) {
  50. let x = getLineNo(curT);
  51. // console.log(x);
  52. lineNo = x;
  53. focusIngredient();
  54. highLight();
  55. if (lineNo < (captureData.length - 1)) {
  56. lineNo++
  57. }
  58. }
  59. });
  60. audio.addEventListener("ended",function(){
  61. goback();
  62. })
  63. }
  64. // 把生成的HTML顯示到介面上
  65. function createHtml(){
  66. let ingredientData = recipeData.ingredient;
  67. // 字幕DOM
  68. for(let i=0;i<captureData.length;i++){
  69. let $li = $("<li></li>").html(captureData[i].capture);
  70. $ul.append($li);
  71. }
  72. $captureContainer.append($ul);
  73. // console.log(captureData.ms.length)
  74. // 食材DOM
  75. for(let i=0;i<ingredientData.length;i++){
  76. $ingredientContainer.append(`
  77. <a href="${ingredientData[i].salepage_url}" class="recipe__ingredient" data-ingredientid="${ingredientData[i].ingredient_id}">
  78. <img src="https://d1xzlli46wohoc.cloudfront.net/images/recipePage/${ingredientData[i].ingredient_img}" alt="">
  79. </a>
  80. `);
  81. if(ingredientData[i].salepage_url == null) {
  82. $('.recipe__ingredient[data-ingredientid="'+ingredientData[i].ingredient_id+'"]').removeAttr("href");
  83. }
  84. // console.log(`${ingredientData[i].ingredient_img}`)
  85. }
  86. $ingredientContainer.width((ingredientData.length*ingredientW)+5);
  87. }
  88. // 解析介面上的lrc數據
  89. function getCapturDetail(){
  90. for (let i=0; i<voiceData.length; i++) {
  91. // match()返回指定的值
  92. let txt = voiceData[i].text
  93. let times = voiceData[i].timecode;
  94. let ingredient = voiceData[i].ingredient_id;
  95. // console.log(voiceData.length)
  96. let secondTime = times.split(":");
  97. // console.log(times.length)
  98. // parseFloat() 將字串轉換為以十進位表示的浮點數
  99. captureData.push({
  100. time: (parseInt(secondTime[0]) * 60 + parseFloat(secondTime[1])).toFixed(3),
  101. capture: txt,
  102. ingredient: ingredient
  103. // 找出一個字串在另一個字串中最後出現的位置
  104. // str.substr(start [, length])
  105. })
  106. }
  107. // console.log(captureData);
  108. // 時間排序
  109. captureData.sort(function(a,b){
  110. return a.time - b.time
  111. });
  112. console.log(captureData)
  113. }
  114. async function showLyric(){
  115. const datail1 = await getCapturDetail();
  116. const detail2 = await createHtml();
  117. // const detail3 = await highLight();
  118. }
  119. // 讓歌詞跟隨音檔播放
  120. function highLight(){
  121. let $li = $captureContainer.find("li");
  122. // console.log(lineNo)
  123. lineHeight = $li.eq(lineNo).position().top;
  124. $li.eq(lineNo).addClass("active").siblings().removeClass("active");
  125. if (lineNo >= 0) {
  126. if (prehighLine == null) {
  127. $captureContainer.stop(true, true).animate({ scrollTop: lineHeight })
  128. prehighLine = lineNo;
  129. // console.log("lineHeight: " + lineHeight)
  130. }
  131. else if (prehighLine != lineNo) {
  132. $captureContainer.stop(true, true).animate({ scrollTop: lineHeight })
  133. prehighLine = lineNo;
  134. // console.log("lineHeight: " + lineHeight)
  135. }
  136. }
  137. }
  138. // 食材隨音檔發亮
  139. function focusIngredient(){
  140. $(".recipe__ingredient").removeClass("active");
  141. if(lineNo>=0){
  142. // 字幕指定的食材
  143. let focusId = captureData[lineNo].ingredient
  144. // console.log("focusId.length: " + focusId.length)
  145. if(focusId.length>0){
  146. // 指定食材添加 active
  147. for(let i=0; i<focusId.length; i++){
  148. $(".recipe__ingredient[data-ingredientid="+focusId[i]+"]").addClass("active");
  149. // console.log("focusId[i]: "+focusId[i])
  150. }
  151. if(preLineNo == null) {
  152. let offsetLeft = $(".recipe__ingredient[data-ingredientid=" + focusId[0] + "]").position().left;
  153. preLineNo = lineNo;
  154. $(".recipe__outer-ingredients").scrollLeft(offsetLeft);
  155. }else if(preLineNo != lineNo) {
  156. let offsetLeft = $(".recipe__ingredient[data-ingredientid=" + focusId[0] + "]").position().left;
  157. preLineNo = lineNo
  158. $(".recipe__outer-ingredients").scrollLeft(offsetLeft);
  159. // console.log("offsetLeft: " + offsetLeft)
  160. // console.log("focusId[0]: " + focusId[0])
  161. }else {
  162. }
  163. }else {
  164. // $(".recipe__outer").scrollLeft(0);
  165. }
  166. }
  167. }
  168. // 當快進或倒退的時候,找到最近的後面那個captureData.ms[i].t
  169. function getLineNo(ct){
  170. // console.log(parseFloat(lineNo))
  171. // console.log(parseFloat(captureData[lineNo].time))
  172. if(ct>=parseFloat(captureData[lineNo].time)) {
  173. // 快進
  174. for(let i=captureData.length - 1; i>=lineNo; i--) {
  175. if(ct>=parseFloat(captureData[i].time)) {
  176. // console.log("大於")
  177. return i;
  178. }
  179. }
  180. }else if(ct === 0){
  181. // console.log('ct=0')
  182. return 1;
  183. }
  184. else {
  185. // 後退
  186. for(let i=0;i<=lineNo; i++) {
  187. if(ct <= parseFloat(captureData[i].time)) {
  188. // console.log("小於")
  189. return i-1
  190. }
  191. }
  192. }
  193. }
  194. // 回到字幕最前面
  195. function goback(){
  196. lineNo = 0;
  197. // $ul.animate({top: 0});
  198. $captureContainer.animate({ scrollTop: 0 });
  199. // highLight();
  200. $(".recipe__outer-ingredients").scrollLeft(0);
  201. $(".recipe__ingredient").removeClass("active");
  202. }
  203. {
  204. $(document).ready(function () {
  205. init();
  206. });
  207. }
  208. //public
  209. return {
  210. intoPage: function(){
  211. intoPage();
  212. },
  213. };
  214. };
  215. var captureFollow = new captureFollow();