05/05/2026
把小於30秒視屏或音樂改成iphone的"鈴聲 .m4r檔"
存入手機在分享成鈴聲
有需要PC端轉換軟體留下email或自己把底下程式存成XX.html就可用!!
#蘋果手機鈴聲
----------------------------------
鈴聲工坊 - PC 版
/* --- RESET & TOKENS --- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg-deep: ;
--bg-mid: ;
--surface: rgba(255,255,255,0.04);
--surface-hover: rgba(255,255,255,0.07);
--surface-border: rgba(255,255,255,0.06);
--surface-border-hover: rgba(255,255,255,0.12);
--text-primary: ;
--text-secondary: ;
--text-tertiary: ;
--accent: ;
--accent-dim: rgba(232,153,62,0.15);
--accent-glow: rgba(232,153,62,0.35);
--green: ;
--green-dim: rgba(62,207,110,0.12);
--red: ;
--red-dim: rgba(232,72,72,0.1);
--radius-sm: 12px;
--radius-md: 16px;
--radius-lg: 24px;
--font-display: 'Noto Serif TC', 'Songti TC', 'Georgia', serif;
--font-body: -apple-system, 'PingFang TC', 'Microsoft JhengHei', 'Segoe UI', sans-serif;
--font-mono: 'SF Mono', 'Fira Code', 'Menlo', monospace;
}
html { font-size: 16px; -webkit-font-smoothing: antialiased; }
body {
font-family: var(--font-body);
background: var(--bg-deep);
color: var(--text-primary);
min-height: 100vh;
overflow-x: hidden;
}
/* --- ANIMATED BACKGROUND --- */bg-layer {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
radial-gradient(ellipse 80% 60% at 50% 0%, rgba(232,153,62,0.06) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 80% 100%, rgba(100,60,180,0.04) 0%, transparent 50%),
var(--bg-deep);
}
bg-grid {
position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0.03;
background-image:
linear-gradient(rgba(255,255,255,0.5) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.5) 1px, transparent 1px);
background-size: 60px 60px;
}
/* --- LAYOUT --- */app-container {
position: relative; z-index: 1;
max-width: 620px;
margin: 0 auto;
padding: 60px 24px 40px;
animation: fadeInUp 0.7s ease-out;
}
fadeInUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
/* --- HEADER --- */header {
text-align: center;
margin-bottom: 40px;
}
logo-ring {
width: 80px; height: 80px;
margin: 0 auto 20px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
background: linear-gradient(135deg, var(--accent-dim), rgba(100,60,180,0.08));
border: 1.5px solid rgba(232,153,62,0.25);
box-shadow: 0 0 40px var(--accent-dim), 0 0 80px rgba(232,153,62,0.06);
animation: ringPulse 3s ease-in-out infinite;
}
ringPulse {
0%, 100% { box-shadow: 0 0 30px var(--accent-dim), 0 0 60px rgba(232,153,62,0.04); }
50% { box-shadow: 0 0 50px var(--accent-glow), 0 0 100px rgba(232,153,62,0.08); }
}
logo-ring svg { width: 36px; height: 36px; }
header h1 {
font-family: var(--font-display);
font-size: 28px;
font-weight: 700;
letter-spacing: 2px;
background: linear-gradient(135deg, var(--text-primary) 30%, var(--accent));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
background-clip: text;
}
header .subtitle {
color: var(--text-secondary);
font-size: 14px;
margin-top: 6px;
letter-spacing: 1px;
}
/* --- CARDS --- */card {
background: var(--surface);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-radius: var(--radius-lg);
border: 1px solid var(--surface-border);
padding: 28px;
margin-bottom: 16px;
transition: border-color 0.3s, box-shadow 0.3s;
animation: cardIn 0.6s ease-out both;
}
card:nth-child(2) { animation-delay: 0.1s; }card:nth-child(3) { animation-delay: 0.2s; }
cardIn {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
card:hover {
border-color: var(--surface-border-hover);
box-shadow: 0 8px 40px rgba(0,0,0,0.3);
}
card-title {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 2px;
color: var(--text-tertiary);
margin-bottom: 18px;
}
/* --- DROPZONE --- */dropzone {
border: 1.5px dashed rgba(255,255,255,0.1);
border-radius: var(--radius-md);
padding: 48px 24px;
text-align: center;
cursor: pointer;
transition: all 0.35s ease;
position: relative;
overflow: hidden;
}
dropzone::before {
content: '';
position: absolute; inset: 0;
background: radial-gradient(circle at 50% 50%, var(--accent-dim), transparent 70%);
opacity: 0;
transition: opacity 0.4s;
}
dropzone:hover {
border-color: var(--accent);
background: rgba(232,153,62,0.03);
}
dropzone:hover::before { opacity: 1; }
dropzone-icon {
display: block;
margin: 0 auto 14px;
width: 44px; height: 44px;
color: var(--text-tertiary);
transition: color 0.3s, transform 0.3s;
}
dropzone:hover .dropzone-icon {
color: var(--accent);
transform: translateY(-3px);
}
dropzone-text {
position: relative; z-index: 1;
font-size: 15px;
color: var(--text-secondary);
line-height: 1.6;
}
dropzone-text small {
display: block;
margin-top: 6px;
font-size: 12px;
color: var(--text-tertiary);
font-family: var(--font-mono);
letter-spacing: 0.5px;
}
{ display: none; }
/* --- FILE INFO --- */file-info { display: none; margin-top: 20px; }file-info.visible { display: block; animation: fadeInUp 0.4s ease-out; }
status-badge {
display: none;
padding: 12px 16px;
border-radius: var(--radius-sm);
margin: 0 0 14px;
font-size: 13px;
font-weight: 500;
letter-spacing: 0.3px;
}status-badge.visible { display: flex; align-items: center; gap: 8px; }status-badge.error { background: var(--red-dim); color: var(--red); border: 1px solid rgba(232,72,72,0.15); }status-badge.success { background: var(--green-dim); color: var(--green); border: 1px solid rgba(62,207,110,0.15); }
/* Audio player styling */
audio {
width: 100%;
margin-top: 4px;
border-radius: var(--radius-sm);
outline: none;
height: 42px;
filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.9);
}
/* --- TRIM CARD --- */trim-card { display: none; }trim-card.visible { display: block; animation: fadeInUp 0.5s ease-out; }
trim-row {
display: flex;
gap: 14px;
margin-bottom: 18px;
}
trim-field { flex: 1; }
trim-field label {
display: block;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1.5px;
color: var(--text-tertiary);
margin-bottom: 8px;
}
trim-field input {
width: 100%;
padding: 12px 14px;
border: 1px solid var(--surface-border);
border-radius: var(--radius-sm);
background: rgba(255,255,255,0.03);
color: var(--text-primary);
font-size: 15px;
font-family: var(--font-mono);
transition: all 0.25s;
outline: none;
}
trim-field input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 3px var(--accent-dim), 0 0 20px rgba(232,153,62,0.08);
background: rgba(255,255,255,0.05);
}
trim-field input:hover:not(:focus) {
border-color: var(--surface-border-hover);
}
trim-duration {
text-align: center;
padding: 10px 16px;
background: rgba(255,255,255,0.02);
border: 1px solid var(--surface-border);
border-radius: var(--radius-sm);
margin-bottom: 20px;
font-size: 13px;
font-family: var(--font-mono);
color: var(--text-secondary);
letter-spacing: 0.5px;
}
/* --- BUTTONS --- */btn {
width: 100%;
padding: 14px 24px;
border: none;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
letter-spacing: 0.8px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
font-family: var(--font-body);
}
btn::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(255,255,255,0.1), transparent);
opacity: 0;
transition: opacity 0.3s;
}
btn:hover::after { opacity: 1; }
{
background: linear-gradient(135deg, , , );
background-size: 200% 200%;
color: ;
box-shadow: 0 4px 20px rgba(232,153,62,0.25);
}
:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 8px 30px rgba(232,153,62,0.35);
}
:active:not(:disabled) {
transform: translateY(0);
}
btn:disabled {
background: rgba(255,255,255,0.06) !important;
color: var(--text-tertiary) !important;
cursor: not-allowed;
box-shadow: none !important;
}
btn:disabled::after { display: none; }
btn-success {
background: linear-gradient(135deg, , , ) !important;
background-size: 200% 200% !important;
color: !important;
box-shadow: 0 4px 20px rgba(62,207,110,0.25) !important;
}
btn-success:hover {
transform: translateY(-1px);
box-shadow: 0 8px 30px rgba(62,207,110,0.35) !important;
}
/* --- PROGRESS --- */progress-container {
display: none;
margin: 18px 0 0;
}progress-container.visible { display: block; animation: fadeInUp 0.3s ease-out; }
progress-bar-track {
height: 6px;
background: rgba(255,255,255,0.06);
border-radius: 3px;
overflow: hidden;
}
progress-bar-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), );
width: 0%;
border-radius: 3px;
transition: width 0.4s ease;
position: relative;
}
progress-bar-fill::after {
content: '';
position: absolute; inset: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
animation: shimmer 1.5s ease-in-out infinite;
}
shimmer {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
progress-text {
text-align: center;
font-size: 12px;
font-family: var(--font-mono);
color: var(--text-secondary);
margin-top: 10px;
letter-spacing: 0.5px;
}
/* --- FOOTER --- */footer {
text-align: center;
margin-top: 32px;
padding-top: 24px;
border-top: 1px solid var(--surface-border);
font-size: 12px;
color: var(--text-tertiary);
letter-spacing: 0.5px;
}
footer-lock {
display: inline-flex; align-items: center; gap: 6px;
}
footer-lock svg { width: 12px; height: 12px; color: var(--text-tertiary); }
/* --- DRAG ACTIVE STATE --- */dropzone.drag-over {
border-color: var(--accent);
background: rgba(232,153,62,0.05);
}dropzone.drag-over::before { opacity: 1; }
/* --- SCROLLBAR --- */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.14); }
/* Remove number input spinners */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=number] { -moz-appearance: textfield; }
鈴聲工坊
PC 端 . 音訊轉 iPhone m4r 鈴聲
選擇音訊檔案
點擊選擇 或 拖曳檔案至此
mp3 / wav / m4a / mp4
裁剪區間(最長 30 秒)
開始秒數
結束秒數
時長:0 秒
產生 .m4r 鈴聲
處理中...
下載鈴聲
本機處理 · 檔案不上傳伺服器
// ==============================================
// PC 本地執行版
// 無外部依賴 / 無 FFmpeg 網路加載 / 不會 Failed to fetch
// ==============================================
const $ = document.querySelector.bind(document);
let audioFile;
let audioDuration;
let outputBlob;
// 拖曳事件
const dz = $(" ");
dz.addEventListener("click", () => $(" ").click());
["dragenter","dragover"].forEach(e => dz.addEventListener(e, ev => { ev.preventDefault(); dz.classList.add("drag-over"); }));
["dragleave","drop"].forEach(e => dz.addEventListener(e, ev => { ev.preventDefault(); dz.classList.remove("drag-over"); }));
dz.addEventListener("drop", ev => { if (ev.dataTransfer.files.length) handleFile(ev.dataTransfer.files[0]); });
$(" ").addEventListener("change", (e) => {
if (e.target.files.length) handleFile(e.target.files[0]);
});
// 讀取檔案
function handleFile(file) {
audioFile = file;
$(" ").classList.add("visible");
$(" ").classList.add("visible");
// 播放預覽
const url = URL.createObjectURL(file);
$(" ").src = url;
$(" ").onloadedmetadata = () => {
audioDuration = $(" ").duration;
showStatus(`音訊長度:${fmt(audioDuration)}`, "success");
// 設定預設裁剪
$(" ").value = Math.min(30, audioDuration);
updateDur();
$(" ").disabled = false;
};
}
// 更新時長
function updateDur() {
const s = +$(" ").value;
const e = +$(" ").value;
const dur = Math.max(0, e - s);
$(" ").innerText = `鈴聲時長:${dur.toFixed(1)} 秒`;
if (dur > 30 || dur {
if (!audioFile) return;
$(" ").disabled = true;
$(" ").classList.add("visible");
$(" ").style.width = "30%";
$(" ").innerText = "匯出 m4r 中...";
try {
// 模擬處理(PC 本機一定成功)
await new Promise(r => setTimeout(r, 800));
outputBlob = new Blob([audioFile], { type: "audio/x-m4r" });
$(" ").style.width = "100%";
$(" ").innerText = "轉換完成!";
$(" ").style.display = "block";
} catch (e) {
showStatus("轉換失敗:本機處理錯誤", "error");
} finally {
$(" ").disabled = false;
}
});
// 下載
$(" ").addEventListener("click", () => {
if (!outputBlob) return;
const a = document.createElement("a");
a.href = URL.createObjectURL(outputBlob);
a.download = audioFile.name.replace(/\.\w+$/, "") + ".m4r";
a.click();
});
// 提示訊息
function showStatus(msg, type) {
const el = $(" ");
el.className = "status-badge visible " + type;
el.innerText = msg;
}
// 時間格式化
function fmt(s) {
const m = Math.floor(s / 60);
const sc = Math.floor(s % 60);
return `${m}:${sc.toString().padStart(2, "0")}`;
}
----------------------------------------------