/* - 파일명: script.js - 위치: /assets/js/ - 기능: 스크립트 발표 시간 계산 및 UI 상호작용 - 작성일: 2025-03-22 - 수정일: 2025-03-22 */ // =================================== // 초기화 및 이벤트 리스너 // =================================== $(document).ready(function() { // 탭 기능 초기화 initTabs(); // 슬라이더 스타일 업데이트 updateSliderStyle(); // 초기화 updateCounts(); // 이벤트 리스너 setupEventListeners(); }); /** * 탭 기능 초기화 */ function initTabs() { $('.tab-button').on('click', function() { // 활성 탭 스타일 제거 $('.tab-button').removeClass('active'); // 현재 탭 활성화 $(this).addClass('active'); // 탭 콘텐츠 표시 const targetId = $(this).attr('data-bs-target'); $('.tab-pane').removeClass('show active'); $(targetId).addClass('show active'); }); } /** * 슬라이더 스타일 업데이트 */ function updateSliderStyle() { const slider = document.getElementById('speedRange'); // 슬라이더 그라데이션 업데이트 function updateSliderGradient() { const value = (slider.value - slider.min) / (slider.max - slider.min) * 100; slider.style.background = `linear-gradient(90deg, var(--primary) 0%, var(--primary) ${value}%, #e0e0e0 ${value}%, #e0e0e0 100%)`; // 현재 속도 퍼센트 표시 업데이트 $('#currentSpeed').text(slider.value + '%'); } // 초기 그라데이션 설정 updateSliderGradient(); // 슬라이더 변경 시 그라데이션 업데이트 slider.addEventListener('input', updateSliderGradient); } /** * 이벤트 리스너 설정 */ function setupEventListeners() { // 입력 이벤트 $('#scriptText').on('input', function() { updateCounts(); }); // 속도 조절 이벤트 $('#speedRange').on('input', function() { calculateSpeechTime(); }); // 이전 버튼 이벤트 리스너 제거 // $('#calculateBtn').on('click', function() { // calculateSpeechTime(); // }); } // =================================== // 텍스트 분석 함수 // =================================== /** * 텍스트 입력값에 따라 문자수, 단어수 등을 업데이트 */ function updateCounts() { const text = $('#scriptText').val(); // 공백 포함 문자 수 const totalChars = text.length; $('#totalChars').text(totalChars); // 공백 포함 바이트 수 (유니코드 고려) const totalBytes = getByteLength(text); $('#totalBytes').text(totalBytes); // 공백 제외 문자 수 const noSpaceText = text.replace(/\s+/g, ''); const noSpaceChars = noSpaceText.length; $('#noSpaceChars').text(noSpaceChars); // 공백 제외 바이트 수 const noSpaceBytes = getByteLength(noSpaceText); $('#noSpaceBytes').text(noSpaceBytes); // 단어 수 (영어, 한글 모두 고려) // 한글은 띄어쓰기를 기준으로, 영어는 정규식으로 단어 분리 const words = text.trim().split(/\s+/).filter(word => word.length > 0); $('#wordCount').text(words.length); // 시간 계산 calculateSpeechTime(); } /** * 문자열의 바이트 길이 계산 (유니코드 고려) * * @param {string} str - 길이를 계산할 문자열 * @return {number} 바이트 길이 */ function getByteLength(str) { let byteLength = 0; for (let i = 0; i < str.length; i++) { const charCode = str.charCodeAt(i); // ASCII 문자는 1바이트 if (charCode < 0x80) { byteLength += 1; } // 한글 및 기타 유니코드 문자는 3바이트 else { byteLength += 3; } } return byteLength; } /** * 발표 시간 계산 함수 * 한국어와 영어의 말하기 속도 차이를 고려 * * @return {void} UI 업데이트 */ function calculateSpeechTime() { const text = $('#scriptText').val(); if (!text.trim()) { $('#speechTime').text('0초'); return; } // 속도 가중치 가져오기 (60-140%) const speedFactor = $('#speedRange').val() / 100; // 한글, 영어, 숫자, 특수문자 분리 const koreanChars = text.match(/[가-힣]/g) || []; const englishChars = text.match(/[a-zA-Z]/g) || []; const numbers = text.match(/[0-9]/g) || []; // 한글, 영어 음절 발음 시간 계산 (가중치 적용) // 평균적으로 한국어는 분당 약 300-350자, 영어는 분당 약 150-170단어 // 한글: 1글자당 약 0.17초, 영어: 1글자당 약 0.08초 (추정치) const koreanTime = koreanChars.length * 0.17; const englishTime = englishChars.length * 0.08; const numberTime = numbers.length * 0.05; // 총 발표 시간 (초 단위, 속도 가중치 반영) let totalSeconds = (koreanTime + englishTime + numberTime) / speedFactor; // 공백과 문장 사이 휴식 시간 추가 (약 20%) totalSeconds *= 1.2; // 애니메이션 효과로 시간 표시 animateTimeDisplay(totalSeconds); } /** * 시간 표시를 애니메이션으로 업데이트 * * @param {number} totalSeconds - 계산된 총 시간(초) */ function animateTimeDisplay(totalSeconds) { // 현재 표시된 시간 문자열에서 숫자만 추출 const currentDisplay = $('#speechTime').text(); const currentValue = parseFloat(currentDisplay.replace(/[^0-9.]/g, '')) || 0; // 목표 값(계산된 시간) const targetValue = totalSeconds; // 애니메이션 속도 및 단계 설정 const duration = 500; // 밀리초 const steps = 20; const stepDuration = duration / steps; // 각 단계별 값 변화량 const increment = (targetValue - currentValue) / steps; // 애니메이션 실행 let currentStep = 0; let currentValue2 = currentValue; const animation = setInterval(() => { currentStep++; currentValue2 += increment; // 애니메이션 종료 조건 if (currentStep >= steps) { clearInterval(animation); currentValue2 = targetValue; // 정확한 최종값 설정 } // 시간 포맷팅 및 표시 updateTimeDisplay(currentValue2); }, stepDuration); } /** * 시간을 포맷팅하여 표시 * * @param {number} seconds - 표시할 시간(초) */ function updateTimeDisplay(seconds) { let timeDisplay; if (seconds < 60) { // 1분 미만은 초 단위로 표시 timeDisplay = Math.round(seconds) + '초'; } else if (seconds < 3600) { // 1시간 미만은 분:초 형식으로 표시 const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.round(seconds % 60); timeDisplay = minutes + '분 ' + remainingSeconds + '초'; } else { // 1시간 이상은 시:분:초 형식으로 표시 const hours = Math.floor(seconds / 3600); const remainingMinutes = Math.floor((seconds % 3600) / 60); const remainingSeconds = Math.round(seconds % 60); timeDisplay = hours + '시간 ' + remainingMinutes + '분 ' + remainingSeconds + '초'; } // UI 업데이트 $('#speechTime').text(timeDisplay); }