বাংলা/हिंदी/English Input Text Reader auto Scroll: select text to start reading
🔊
বাংলায় শুনুন
Code:
Html:
<!-- 📱 Media query -->
<style>
@media screen and (max-width: 768px) {
#smartReaderBtn {
right: 5px !important;
top: 170px !important;
}
}
</style>
<!-- 📝 Input Box & Button -->
<div style="font-family: sans-serif; margin: 20px auto; max-width: 700px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 10px;">
<p id="wordCount" style="color: #555; margin: 0;"></p>
<div id="smartReaderBtn" onclick="toggleSmartReader()" style="align-items: center; background-color: #ff9900; border-radius: 50%; border: 2px solid white; box-shadow: rgba(0,0,0,0.1) 0px 4px 8px; color: white; cursor: pointer; display: flex; padding: 10px; position: fixed; right: 10%; top: 15%; z-index: 999;">
<span style="font-size: 24px; margin-right: 5px;">🔊</span>
<span id="smartReaderLabel" style="font-size: 16px;">বাংলায় শুনুন</span>
</div>
</div>
<label style="display: block; font-weight: bold; margin-bottom: 10px;">✍️ Enter your text (Limit: 1,500 words):</label>
<textarea class="post-body" id="wordInput" placeholder="তোমার লেখা এখানে দাও।
अपना पाठ यहां डालें।
Put your text here." rows="15" style="border-radius: 8px; border: 1px solid rgb(204, 204, 204); font-size: 16px; padding: 12px; width: 95%;"></textarea>
</div>
JS:
<script>
let smartQueue = [];
let currentIndex = 0;
let isReading = false;
let isPaused = false;
let wakeLock = null;
// 🔁 Toggle start/pause/resume
function toggleSmartReader() {
if (!isReading && !isPaused) {
startSmartReader();
} else if (isReading) {
pauseSmartReader();
} else if (isPaused) {
resumeFromCursor();
}
}
// 🚀 Start reading
function startSmartReader() {
const textarea = document.getElementById("wordInput");
const text = textarea.value.trim();
if (!text) return alert("❌ Enter some text first.");
smartQueue = text
.split(/(?<=[।!?.\n])\s+/)
.map(s => s.trim())
.filter(Boolean);
if (smartQueue.length === 0) {
alert("❌ No readable content.");
return;
}
currentIndex = 0;
isReading = true;
isPaused = false;
updateButtonUI("reading");
requestWakeLock();
readNextLine();
}
// 🔊 Read sentence & scroll
function readNextLine() {
const textarea = document.getElementById("wordInput");
if (currentIndex >= smartQueue.length) {
stopSmartReader();
return;
}
const fullText = textarea.value;
const sentence = smartQueue[currentIndex++];
const index = fullText.indexOf(sentence);
if (index !== -1) {
textarea.focus();
textarea.setSelectionRange(index, index + sentence.length);
scrollToCenter(textarea, index);
}
window.speechSynthesis.cancel();
const speak = new SpeechSynthesisUtterance(sentence);
speak.lang = "bn-IN"; // 🔁 Use "en-US" for English
speak.rate = 1;
speak.pitch = 1;
speak.volume = 1;
speak.onend = () => {
if (isReading) setTimeout(readNextLine, 300);
};
window.speechSynthesis.speak(speak);
}
// 🎯 Accurate scroll to center using mirrored div
function scrollToCenter(textarea, index) {
const beforeText = textarea.value.substring(0, index);
const mirrorDiv = document.createElement("div");
const style = window.getComputedStyle(textarea);
mirrorDiv.style.position = "absolute";
mirrorDiv.style.visibility = "hidden";
mirrorDiv.style.whiteSpace = "pre-wrap";
mirrorDiv.style.wordWrap = "break-word";
mirrorDiv.style.padding = style.padding;
mirrorDiv.style.font = style.font;
mirrorDiv.style.lineHeight = style.lineHeight;
mirrorDiv.style.width = `${textarea.clientWidth}px`;
mirrorDiv.textContent = beforeText;
document.body.appendChild(mirrorDiv);
const pixelOffset = mirrorDiv.offsetHeight;
textarea.scrollTo({
top: pixelOffset - textarea.clientHeight / 2,
behavior: "smooth"
});
document.body.removeChild(mirrorDiv);
}
// ⏸ Pause reading
function pauseSmartReader() {
window.speechSynthesis.cancel();
isReading = false;
isPaused = true;
releaseWakeLock();
updateButtonUI("paused");
setTimeout(() => {
const choose = confirm("📍 Select a point in the box to resume.\n\nOK = Place cursor\nCancel = Stop");
if (!choose) {
isPaused = false;
updateButtonUI("default");
}
}, 200);
}
// ▶️ Resume from cursor
function resumeFromCursor() {
const textarea = document.getElementById("wordInput");
const pos = textarea.selectionStart;
const fullText = textarea.value;
let startIndex = 0;
for (let i = 0; i < smartQueue.length; i++) {
const idx = fullText.indexOf(smartQueue[i]);
if (idx >= 0 && idx <= pos) {
startIndex = i;
}
}
currentIndex = startIndex;
setTimeout(() => {
const resume = confirm("✅ Start reading from selected point?");
if (resume) {
isPaused = false;
isReading = true;
updateButtonUI("reading");
requestWakeLock();
readNextLine();
} else {
isPaused = false;
updateButtonUI("default");
}
}, 100);
}
// 🛑 Stop everything
function stopSmartReader() {
isReading = false;
isPaused = false;
currentIndex = 0;
updateButtonUI("default");
window.speechSynthesis.cancel();
releaseWakeLock();
}
// 🔁 Update button
function updateButtonUI(state) {
const btn = document.getElementById("smartReaderBtn");
const label = document.getElementById("smartReaderLabel");
if (state === "reading") {
btn.style.backgroundColor = "lightskyblue";
label.textContent = "Reading...";
} else if (state === "paused") {
btn.style.backgroundColor = "#f0ad4e";
label.textContent = "Paused — Resume?";
} else {
btn.style.backgroundColor = "#ff9900";
label.textContent = "Listen in Bengali/English";
}
}
// 🔋 Wake Lock Request
async function requestWakeLock() {
if ('wakeLock' in navigator) {
try {
wakeLock = await navigator.wakeLock.request('screen');
} catch (err) {
console.warn("❌ Wake Lock error:", err);
}
}
}
// 🔓 Release Wake Lock
async function releaseWakeLock() {
if (wakeLock) {
try {
await wakeLock.release();
wakeLock = null;
} catch (err) {
console.warn("❌ Release Wake Lock failed:", err);
}
}
}
// 🔄 Resume wake lock on return
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible" && isReading) {
requestWakeLock();
}
});
// 📝 Word Count
const textarea = document.getElementById("wordInput");
const countDisplay = document.getElementById("wordCount");
const maxWords = 1500;
textarea.addEventListener("input", () => {
const text = textarea.value.trim();
const words = text === "" ? [] : text.split(/\s+/);
const count = words.length;
if (count > maxWords) {
countDisplay.innerHTML = `❌ Limit exceeded: <strong>${count}</strong> / ${maxWords}`;
countDisplay.style.color = "red";
} else {
countDisplay.innerHTML = `✅ Word Count: <strong>${count}</strong> / ${maxWords}`;
countDisplay.style.color = "green";
}
});
</script>
Comments
Post a Comment