播放音频

我们需要点击卡片,播放音频。

请先把音频mp3文件,放到www/audio目录下面

我们是演示项目,所以这里把一个b.mp3放到目录中就可以。

方式1-HTML5播放

通过WEBVIEW的支持的HTML5 Audio来播放音频,无序插件

修改main.js 内容如下

// Dữ liệu phụ âm tiếng Việt
const consonants = [
    {char: "b", pronounce: "/ɓ/", desc: "Phụ âm bật môi"},
    {char: "c", pronounce: "/k/", desc: "Phụ âm vòm ngạc cứng"},
    {char: "ch", pronounce: "/tɕ/", desc: "Phụ âm xát vòm"},
    {char: "d", pronounce: "/ɗ/", desc: "Phụ âm bật răng lợi"},
    {char: "đ", pronounce: "/ɗ/", desc: "Phụ âm đệm răng lợi"},
    {char: "g", pronounce: "/ɣ/", desc: "Phụ âm xát vòm ngạc"},
    {char: "gh", pronounce: "/ɣ/", desc: "Dùng trước e, i"},
    {char: "h", pronounce: "/h/", desc: "Phụ âm thanh hơi"},
    {char: "k", pronounce: "/k/", desc: "Phụ âm tắc ngạc cứng"},
    {char: "kh", pronounce: "/x/", desc: "Phụ âm xát ngạc cứng"},
    {char: "l", pronounce: "/l/", desc: "Phụ âm bên răng lợi"},
    {char: "m", pronounce: "/m/", desc: "Phụ âm mũi môi"},
    {char: "n", pronounce: "/n/", desc: "Phụ âm mũi răng lợi"},
    {char: "ng", pronounce: "/ŋ/", desc: "Phụ âm mũi ngạc mềm"},
    {char: "ngh", pronounce: "/ŋ/", desc: "Dùng trước e,i"},
    {char: "nh", pronounce: "/ɲ/", desc: "Phụ âm mũi vòm ngạc"},
    {char: "p", pronounce: "/p/", desc: "Phụ âm tắc môi không bật"},
    {char: "ph", pronounce: "/f/", desc: "Phụ âm xát môi răng"},
    {char: "q", pronounce: "/kw/", desc: "Luôn đi với u"},
    {char: "r", pronounce: "/r/", desc: "Phụ âm rung răng lợi"},
    {char: "s", pronounce: "/s/", desc: "Phụ âm xát răng lợi"},
    {char: "t", pronounce: "/t/", desc: "Phụ âm tắc răng lợi"},
    {char: "th", pronounce: "/θ/", desc: "Phụ âm xát răng"},
    {char: "v", pronounce: "/v/", desc: "Phụ âm xát môi răng"},
    {char: "x", pronounce: "/s/", desc: "Phụ âm xát răng lợi"}
];

let favList = JSON.parse(localStorage.getItem("vn_consonant_fav")) || [];
let currentAudio = null; // 全局音频对象,用于停止上一首

const gridEl = document.getElementById("consonant-grid");
const favGridEl = document.getElementById("favorite-grid");
const modal = document.getElementById("modal");
const modalChar = document.getElementById("modal-char");
const modalPro = document.getElementById("modal-pronounce");
const modalDesc = document.getElementById("modal-desc");
const btnFav = document.getElementById("btn-fav");
const closeModal = document.getElementById("close-modal");

let currentItem = null;

// ====================== HÀM PHÁT MP3 ======================
function playAudio(charName) {
    // Dừng âm thanh đang phát
    if(currentAudio){
        currentAudio.pause();
        currentAudio.currentTime = 0;
    }
    // Tạo audio
    currentAudio = new Audio(`audio/${charName}.mp3`);
    currentAudio.volume = 1;
    currentAudio.play().catch(err=>{
        console.log("Không phát được file: ", charName, err);
        alert(`Không tìm thấy file âm thanh: ${charName}.mp3`);
    });
}

// Render thẻ phụ âm
function renderCards(){
    gridEl.innerHTML = "";
    consonants.forEach(item=>{
        const div = document.createElement("div");
        div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
        div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
        div.onclick = ()=> {
            playAudio(item.char); // Nhấn thẻ phát mp3
            openModal(item);
        };
        gridEl.appendChild(div);
    })
}

// Render trang yêu thích
function renderFav(){
    favGridEl.innerHTML = "";
    if(favList.length === 0){
        favGridEl.innerHTML = `<p class="col-span-3 text-center text-gray-400 py-10">Chưa có từ yêu thích</p>`;
        return;
    }
    favList.forEach(item=>{
        const div = document.createElement("div");
        div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
        div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
        div.onclick = ()=> {
            playAudio(item.char);
            openModal(item);
        };
        favGridEl.appendChild(div);
    })
}

// Mở popup chi tiết
function openModal(item){
    currentItem = item;
    modalChar.innerText = item.char;
    modalPro.innerText = item.pronounce;
    modalDesc.innerText = item.desc;
    updateFavButton();
    modal.classList.remove("hidden");
}

// Đóng popup
closeModal.onclick = ()=>{
    modal.classList.add("hidden");
}
modal.onclick = (e)=>{
    if(e.target === modal) modal.classList.add("hidden");
}

// Nút thêm/xóa yêu thích
function updateFavButton(){
    const exist = favList.find(x=>x.char === currentItem.char);
    btnFav.innerText = exist ? "Xóa khỏi yêu thích" : "Thêm yêu thích";
}
btnFav.onclick = ()=>{
    const idx = favList.findIndex(x=>x.char === currentItem.char);
    if(idx >=0) favList.splice(idx,1);
    else favList.push(currentItem);
    localStorage.setItem("vn_consonant_fav", JSON.stringify(favList));
    updateFavButton();
    renderFav();
}

// Tab chuyển trang
const tabBtns = document.querySelectorAll(".tab-btn");
const pages = document.querySelectorAll(".page");
tabBtns.forEach(btn=>{
    btn.onclick = ()=>{
        const targetId = btn.dataset.target;
        tabBtns.forEach(b=>{
            b.classList.remove("tab-active");
            b.querySelector("svg").classList.add("text-gray-500");
        })
        btn.classList.add("tab-active");
        btn.querySelector("svg").classList.remove("text-gray-500");
        pages.forEach(p=>p.classList.add("hidden"));
        document.getElementById(targetId).classList.remove("hidden");
    }
})

document.addEventListener("DOMContentLoaded", ()=>{
    renderCards();
    renderFav();
})

document.addEventListener("deviceready", ()=>{
    console.log("Cordova sẵn sàng");
}, false);Code language: JavaScript (javascript)

方案2-插件播放

通过cordova插件cordova-plugin-media 来播放

安装插件

cordova plugin add cordova-plugin-media

执行过程

D:\cordovademo\hello>cordova plugin add cordova-plugin-media
Installing "cordova-plugin-media" for android
Installing "cordova-plugin-file" for android
Subproject Path: CordovaLib
Subproject Path: app

The Android Persistent storage location now defaults to "Internal". Please check this plugin's README to see if your application needs any changes in its config.xml.

If this is a new application no changes are required.

If this is an update to an existing application that did not specify an "AndroidPersistentFileLocation" you may need to add:

      "<preference name="AndroidPersistentFileLocation" value="Compatibility" />"

to config.xml in order for the application to find previously stored files.

Adding cordova-plugin-media to package.jsonCode language: HTML, XML (xml)

音频播放函数替换

let mediaPlayer = null;

function playAudio(charName) {
    // Dừng file cũ
    if(mediaPlayer){
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }
    // Quan trọng: Cordova Android đường dẫn www/audio/
    const src = `audio/${charName}.mp3`;
    mediaPlayer = new Media(src,
        ()=>{
            // Phát xong giải phóng
            mediaPlayer.release();
            mediaPlayer = null;
        },
        (err)=>{
            console.error("Lỗi phát âm thanh", err);
        }
    );
    mediaPlayer.play();
}Code language: JavaScript (javascript)

替换上面为原来的main.js 里面的方法

// Dữ liệu phụ âm tiếng Việt
const consonants = [
    {char: "b", pronounce: "/ɓ/", desc: "Phụ âm bật môi"},
    {char: "c", pronounce: "/k/", desc: "Phụ âm vòm ngạc cứng"},
    {char: "ch", pronounce: "/tɕ/", desc: "Phụ âm xát vòm"},
    {char: "d", pronounce: "/ɗ/", desc: "Phụ âm bật răng lợi"},
    {char: "đ", pronounce: "/ɗ/", desc: "Phụ âm đệm răng lợi"},
    {char: "g", pronounce: "/ɣ/", desc: "Phụ âm xát vòm ngạc"},
    {char: "gh", pronounce: "/ɣ/", desc: "Dùng trước e, i"},
    {char: "h", pronounce: "/h/", desc: "Phụ âm thanh hơi"},
    {char: "k", pronounce: "/k/", desc: "Phụ âm tắc ngạc cứng"},
    {char: "kh", pronounce: "/x/", desc: "Phụ âm xát ngạc cứng"},
    {char: "l", pronounce: "/l/", desc: "Phụ âm bên răng lợi"},
    {char: "m", pronounce: "/m/", desc: "Phụ âm mũi môi"},
    {char: "n", pronounce: "/n/", desc: "Phụ âm mũi răng lợi"},
    {char: "ng", pronounce: "/ŋ/", desc: "Phụ âm mũi ngạc mềm"},
    {char: "ngh", pronounce: "/ŋ/", desc: "Dùng trước e,i"},
    {char: "nh", pronounce: "/ɲ/", desc: "Phụ âm mũi vòm ngạc"},
    {char: "p", pronounce: "/p/", desc: "Phụ âm tắc môi không bật"},
    {char: "ph", pronounce: "/f/", desc: "Phụ âm xát môi răng"},
    {char: "q", pronounce: "/kw/", desc: "Luôn đi với u"},
    {char: "r", pronounce: "/r/", desc: "Phụ âm rung răng lợi"},
    {char: "s", pronounce: "/s/", desc: "Phụ âm xát răng lợi"},
    {char: "t", pronounce: "/t/", desc: "Phụ âm tắc răng lợi"},
    {char: "th", pronounce: "/θ/", desc: "Phụ âm xát răng"},
    {char: "v", pronounce: "/v/", desc: "Phụ âm xát môi răng"},
    {char: "x", pronounce: "/s/", desc: "Phụ âm xát răng lợi"}
];

let favList = JSON.parse(localStorage.getItem("vn_consonant_fav")) || [];
let currentAudio = null; // 全局音频对象,用于停止上一首

const gridEl = document.getElementById("consonant-grid");
const favGridEl = document.getElementById("favorite-grid");
const modal = document.getElementById("modal");
const modalChar = document.getElementById("modal-char");
const modalPro = document.getElementById("modal-pronounce");
const modalDesc = document.getElementById("modal-desc");
const btnFav = document.getElementById("btn-fav");
const closeModal = document.getElementById("close-modal");

let currentItem = null;

// ====================== HÀM PHÁT MP3 ======================
let mediaPlayer = null;

function playAudio(charName) {
    // Dừng file cũ
    if(mediaPlayer){
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }
    // Quan trọng: Cordova Android đường dẫn www/audio/
    const src = `audio/${charName}.mp3`;
    mediaPlayer = new Media(src,
        ()=>{
            // Phát xong giải phóng
            mediaPlayer.release();
            mediaPlayer = null;
        },
        (err)=>{
            console.error("Lỗi phát âm thanh", err);
        }
    );
    mediaPlayer.play();
}

// Render thẻ phụ âm
function renderCards(){
    gridEl.innerHTML = "";
    consonants.forEach(item=>{
        const div = document.createElement("div");
        div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
        div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
        div.onclick = ()=> {
            playAudio(item.char); // Nhấn thẻ phát mp3
            openModal(item);
        };
        gridEl.appendChild(div);
    })
}

// Render trang yêu thích
function renderFav(){
    favGridEl.innerHTML = "";
    if(favList.length === 0){
        favGridEl.innerHTML = `<p class="col-span-3 text-center text-gray-400 py-10">Chưa có từ yêu thích</p>`;
        return;
    }
    favList.forEach(item=>{
        const div = document.createElement("div");
        div.className = "bg-cardBg rounded-lg py-5 text-center card-shadow active:scale-95 transition-transform cursor-pointer";
        div.innerHTML = `<span class="text-2xl font-bold text-gray-800">${item.char}</span>`;
        div.onclick = ()=> {
            playAudio(item.char);
            openModal(item);
        };
        favGridEl.appendChild(div);
    })
}

// Mở popup chi tiết
function openModal(item){
    currentItem = item;
    modalChar.innerText = item.char;
    modalPro.innerText = item.pronounce;
    modalDesc.innerText = item.desc;
    updateFavButton();
    modal.classList.remove("hidden");
}

// Đóng popup
closeModal.onclick = ()=>{
    modal.classList.add("hidden");
}
modal.onclick = (e)=>{
    if(e.target === modal) modal.classList.add("hidden");
}

// Nút thêm/xóa yêu thích
function updateFavButton(){
    const exist = favList.find(x=>x.char === currentItem.char);
    btnFav.innerText = exist ? "Xóa khỏi yêu thích" : "Thêm yêu thích";
}
btnFav.onclick = ()=>{
    const idx = favList.findIndex(x=>x.char === currentItem.char);
    if(idx >=0) favList.splice(idx,1);
    else favList.push(currentItem);
    localStorage.setItem("vn_consonant_fav", JSON.stringify(favList));
    updateFavButton();
    renderFav();
}

// Tab chuyển trang
const tabBtns = document.querySelectorAll(".tab-btn");
const pages = document.querySelectorAll(".page");
tabBtns.forEach(btn=>{
    btn.onclick = ()=>{
        const targetId = btn.dataset.target;
        tabBtns.forEach(b=>{
            b.classList.remove("tab-active");
            b.querySelector("svg").classList.add("text-gray-500");
        })
        btn.classList.add("tab-active");
        btn.querySelector("svg").classList.remove("text-gray-500");
        pages.forEach(p=>p.classList.add("hidden"));
        document.getElementById(targetId).classList.remove("hidden");
    }
})

document.addEventListener("DOMContentLoaded", ()=>{
    renderCards();
    renderFav();
})

document.addEventListener("deviceready", ()=>{
    console.log("Cordova sẵn sàng");
}, false);Code language: JavaScript (javascript)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注