杨帆 发表于 2026-7-3 10:33

山歌追上云朵

本帖最后由 杨帆 于 2026-7-3 20:32 编辑 <br /><br /><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="referrer" content="never"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>山歌追上云朵</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Liu+Jian+Mao+Cao&family=Long+Cang&family=Ma+Shan+Zheng&family=ZCOOL+KuaiLe&family=ZCOOL+QingKe+HuangYou&display=swap");
#papa{position:relative;display:grid;place-items:center;margin:30px 0;width:1280px;height:750px;left:calc(50% - 81px);transform:translateX(-50%);box-shadow:3px 3px 6px gray;overflow:hidden;z-index:1;background:#000 url('') no-repeat center/cover;font-family:"ZCOOL KuaiLe","Ma Shan Zheng","Long Cang","华文行楷","SimHei","Arial","sans-serif";--state:running;}
.bg-canvas{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;z-index:0;pointer-events:none;}
.bg-layer{position:absolute;inset:0;background-image:url("https://pic1.imgdb.cn/item/6a44fb8ed82989bc6aad93ac.jpg");background-repeat:no-repeat;background-position:center;background-size:100% 100%;z-index:1;transition:background-image 0.2s ease;}
.papa.play .bg-layer{background-image:url("https://pic1.imgdb.cn/item/68a9ac9258cb8da5c846c1c1.jpg");}
#svgObj{position:absolute;left:50%;top:10%;transform:translateX(-50%);width:100vw;height:50vw;z-index:1;fill:none;stroke:url(#grdWave);stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:2 4 2;stroke-dashoffset:0;opacity:.35;animation:pMove 8s ease-in-out infinite alternate var(--state);}
@keyframes pMove{to{stroke-dashoffset:-180;}}
.vid{position:absolute;z-index:2;left:-10%;top:-10%;width:120%;height:120%;object-fit:cover;pointer-events:none;object-position:center;mask:linear-gradient(to bottom,red 20%,transparent 58%);-webkit-mask:linear-gradient(to bottom,red 20%,transparent 58%);}
#vid{position: absolute; top:-20%;left:-20%; width: 140%; height: 140%; object-fit: cover; pointer-events: none; opacity: 0.9;mix-blend-mode: screen;object-position:center;mask: linear-gradient(to bottom, red 40%, transparent 80%, transparent);-webkit-mask: linear-gradient(to bottom, red 40%, transparent 80%, transparent);pointer-events: none;z-index:3;transition:opacity .5s ease;animation:opa 10s infinite linear;}
@keyframes opa{to{opacity:0;}}
#canvas{position:absolute;z-index:5;left:-180px;top:10%;}
#msvg{position:absolute;z-index:15;right:45px;top:45px;width:120px;height:120px;display:block;cursor:pointer;animation:float 3s ease-in-out infinite alternate var(--state);}
@keyframes float{0%{transform:translateY(0) scale(0.8);}100%{transform:translateY(-20px) scale(1.2);}}
#lyricBox{width:1000px;height:200px;position:absolute;bottom:80px;left:calc(50% + 20px);transform:translateX(-50%);z-index:4;pointer-events:none;}
#mainCanvas{width:1000px;height:140px;position:absolute;top:0;left:0;background:transparent;}
.control{position:absolute;bottom:50px;left:50%;transform:translateX(-50%);display:flex;gap:16px;align-items:center;z-index:15;}
#timeText{color:hsl(36 64% 95% / 0.8);font-family:Consolas;}
#fullscreen{position:absolute;top:20px;left:20px;font:normal 1.5em 楷体;color:#fff;text-shadow:0 0 3px #000;opacity:1;cursor:pointer;z-index:10;}
#fullscreen:hover{font-style:italic;}
</style>
</head>
<body>
<div id="papa" class="papa">
<audio id="aud" src="http://music.163.com/song/media/outer/url?id=3389421509.mp3" playsinline webkit-playsinline autoplay loop ></audio>
<span id="fullscreen" title="">全屏欣赏</span>
<div id="lyricBox">
<canvas id="mainCanvas" width="1000" height="140"></canvas>
</div>
<div class="control">
<span id="timeText">00:00 / 00:00</span>
</div>
<canvas id="canvas" width="550" height="550"></canvas>
<video class="vid" src="https://img2.tukuppt.com/video_show/2414777/00/02/13/5b514e39921d5.mp4" playsinline autoplay loop muted></video>
<video class="vid" src="https://img2.tukuppt.com/video_show/2418175/00/02/01/5b4f55c95539b.mp4" playsinline autoplay loop muted></video>
<video id="vid" src="https://i.uik.cc/view.php/fe397b631457572a1f10840efec0e8a1.mp4" autoplay preload="auto" playsinline loop muted></video>
<div class="bg-layer"></div>
<svg id="svgObj" viewBox="0 0 160 90">
<defs>
<linearGradient id="grdWave">
<stop offset="0" stop-color="#072afc"></stop>
<stop offset="0.15" stop-color="#1976D2"></stop>
<stop offset="1" stop-color="#2196F3"></stop>
</linearGradient>
</defs>
<symbol id="pb">
<path stroke="url(#grdWave)" d='M0 45 C40 0 120 90 160 45'></path>
</symbol>
<use href="#pb" x="-10" y="0"></use>
<use href="#pb" x="-5" y="5"></use>
<use href="#pb" x="0" y="10"></use>
</svg>
<svg id="msvg" width="280" height="280" viewBox="0 0 576 512">
<title id="svgTip">暂停</title>
<defs id="defs">
<radialGradient id="grd" cx="55%" cy="45%" r="60%">
<stop offset="0%" stop-color="#00FFFF"></stop>
<stop offset="50%" stop-color="#6495ED"></stop>
<stop offset="100%" stop-color="#7B68EE"></stop>
</radialGradient>
</defs>
<path fill="url(#grd)" fill-rule="nonzero" d="M385.2 167.6c6.4 0 12.6.3 18.8 1.1C387.4 90.3 303.3 32 207.7 32 100.5 32 13 104.8 13 197.4c0 53.4 29.3 97.5 77.9 131.6l-19.3 58.6 68-34.1c24.4 4.8 43.8 9.7 68.2 9.7 6.2 0 12.1-.3 18.3-.8-4-12.9-6.2-26.6-6.2-40.8-.1-84.9 72.9-154 165.3-154zm-104.5-52.9c14.5 0 24.2 9.7 24.2 24.4 0 14.5-9.7 24.2-24.2 24.2-14.8 0-29.3-9.7-29.3-24.2.1-14.7 14.6-24.4 29.3-24.4zm-136.4 48.6c-14.5 0-29.3-9.7-29.3-24.2 0-14.8 14.8-24.4 29.3-24.4 14.8 0 24.4 9.7 24.4 24.4 0 14.6-9.6 24.2-24.4 24.2zM563 319.4c0-77.9-77.9-141.3-165.4-141.3-92.7 0-165.4 63.4-165.4 141.3S305 460.7 397.6 460.7c19.3 0 38.9-5.1 58.6-9.9l53.4 29.3-14.8-48.6C534 402.1 563 363.2 563 319.4zm-219.1-24.5c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.8 0 24.4 9.7 24.4 19.3 0 10-9.7 19.6-24.4 19.6zm107.1 0c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.5 0 24.4 9.7 24.4 19.3.1 10-9.9 19.6-24.4 19.6z"/>
</svg>
</div>
<script>
(function(){
let gradientAnimId=null;
let lyricAnimId=null;
let gradientOffsetY=0;
const gradientSpeed=1.2;
const msvg=document.getElementById('msvg');
const svgTip=document.getElementById('svgTip');
const wave=document.getElementById('svgObj');
const paBox=document.getElementById('papa');
const vids=document.querySelectorAll('.vid');
const vid1=document.getElementById('vid');
const audioPlayer=document.getElementById('aud');
const canvas=document.getElementById("canvas");
const ctx=canvas.getContext("2d");
let isPaused=false;
function togglePlayPause(){
if(isPaused){
isPaused=false;
audioPlayer.play();
}else{
isPaused=true;
audioPlayer.pause();
}
}
msvg.onclick=togglePlayPause;
audioPlayer.addEventListener('play',function(){
paBox.classList.add('play');
svgTip.textContent="暂停";
paBox.style.setProperty('--state','running');
vids.forEach(vid=>vid.play());
vid1.play();
if(!gradientAnimId)gradientAnimate();
});
audioPlayer.addEventListener('pause',function(){
paBox.classList.remove('play');
svgTip.textContent="播放";
paBox.style.setProperty('--state','paused');
vids.forEach(vid=>vid.pause());
vid1.pause();
cancelAnimationFrame(gradientAnimId);
gradientAnimId=null;
});
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
const lineargradient=ctx.createLinearGradient(350,0+gradientOffsetY,350,750+gradientOffsetY);
lineargradient.addColorStop(0,"#2E8B57");
lineargradient.addColorStop(0.15,"#ff7f00");
lineargradient.addColorStop(0.30,"#ffff00");
lineargradient.addColorStop(0.45,"#00ff00");
lineargradient.addColorStop(0.60,"#00bfff");
lineargradient.addColorStop(0.75,"#ff00ff");
lineargradient.addColorStop(1,"#ff0000");
ctx.font="56px Ma Shan Zheng,华文行楷,serif";
ctx.strokeStyle=lineargradient;
ctx.lineWidth=2;
ctx.textAlign='center';
ctx.textBaseline='middle';
const text="山歌追上云朵";
const x=275;
let startY=100;
const wordGap=70;
for(let i=0;i<text.length;i++){
const char=text;
ctx.strokeText(char,x,startY+i*wordGap);
}
}
function gradientAnimate(){
gradientOffsetY+=gradientSpeed;
if(gradientOffsetY>750)gradientOffsetY=0;
draw();
gradientAnimId=requestAnimationFrame(gradientAnimate);
}
gradientAnimate();
const LRC_RAW=`
山歌追上云朵
演唱:央金拉姆
东边的雪山亮得像白银
我站在山脊喊你的名字
风把我的声音送到三座山外
你听见了吗 放马的人
我的歌声翻过那山顶
我的爱比那山路还长
天上的云朵为我让路
我要翻过最高最高的山
去年你说春天来接我
雪化了三遍你还没来
我剪下牦牛尾巴编成鞭子
抽在山石上 山石裂开
我的歌声翻过那山顶
我的爱比那山路还长
天上的云朵为我让路
我要翻过最高最高的山
你把我的歌装进酒壶里
走到哪里就喝到哪里
喝完这一口 朝北走
我的篝火亮到天亮
我的歌声翻过那山顶
我的爱比那山路还长
天上的云朵为我让路
我要翻过最高最高的山
最高最高的山
最高的山
`;
function parseLrc(lrcStr){
const lines=lrcStr.trim().split(/\r?\n/);
const result=[];
const timeReg=/\[(\d{2}):(\d{2})\.(\d{2,3})\]/;
for(const line of lines){
const match=line.match(timeReg);
if(!match)continue;
const min=parseInt(match);
const sec=parseInt(match);
const ms=parseInt(match);
const time=min*60+sec+ms/1000;
const text=line.replace(timeReg,'').trim();
if(text)result.push({time,text});
}
result.sort((a,b)=>a.time-b.time);
return result;
}
const LRC_DATA=parseLrc(LRC_RAW);
function expandLyrics(data){
if(!Array.isArray(data)||data.length===0){
console.warn('expandLyrics: 无效的歌词数据');
return [];
}
const chars=[];
for(let i=0;i<data.length;i++){
const item=data;
const text=item.text;
const startTime=item.time;
const nextTime=(i<data.length-1)?data.time:startTime+6;
const duration=nextTime-startTime;
const totalChars=text.length;
const charDuration=duration/totalChars;
for(let j=0;j<totalChars;j++){
chars.push({
char:text,
startTime:startTime+j*charDuration-1.0,
duration:charDuration*0.9,
lineIndex:i
});
}
}
return chars;
}
const charData=expandLyrics(LRC_DATA);
const mainCanvas=document.getElementById('mainCanvas');
const mainCtx=mainCanvas.getContext('2d');
const W=1000;
const H=200;
const baseFontSize=56;
const colors=['#ffd700','#00FFFF','#fa5f57','#F8F3EB','#fff887','#fc89e5','#fd27d2','#9cf9fc','#f0e6d2','#0dd7de','#fbcb6d','#fb2f39','#fb8739'];
function drawChar(ctx,char,x,y,fontSize,color,progress){
if(progress<=0)return;
ctx.save();
ctx.font=`bold ${fontSize}px "Long Cang","ZCOOL KuaiLe","华文楷体",KaiTi,STKaiti,serif`;
ctx.textAlign='center';
ctx.textBaseline='middle';
ctx.strokeStyle='rgba(248, 243, 235,0.88)';
ctx.lineWidth=3.0;
ctx.lineJoin='round';
ctx.strokeText(char,x,y);
const maxR=fontSize*0.88;
const r=progress*maxR;
ctx.beginPath();
ctx.arc(x,y,r,0,Math.PI*2);
ctx.clip();
ctx.fillStyle=color;
ctx.fillText(char,x,y);
if(progress<1){
ctx.globalAlpha=(1-progress)*0.4;
ctx.strokeStyle='rgba(255,215,0,0.3)';
ctx.lineWidth=1.5;
ctx.strokeText(char,x,y);
}
ctx.restore();
}
function layoutLine(text,fontSize,centerX,centerY){
const chars=text.split('');
const spacing=fontSize*1.05;
const totalW=chars.length*spacing;
const startX=centerX-totalW/2+spacing/2;
const positions=[];
for(let i=0;i<chars.length;i++){
positions.push({
char:chars,
x:startX+i*spacing,
y:centerY
});
}
return positions;
}
function getCurrentLineIndex(currentTime){
let idx=0;
for(let i=0;i<LRC_DATA.length;i++){
if(currentTime>=LRC_DATA.time)idx=i;
}
return idx;
}
function render(currentTime){
mainCtx.clearRect(0,0,W,H);
const grad=mainCtx.createRadialGradient(W/2,H/2,100,W/2,H/2,500);
grad.addColorStop(0,'rgba(120,30,10,0.10)');
grad.addColorStop(1,'rgba(0,0,0,0)');
mainCtx.fillStyle=grad;
mainCtx.fillRect(0,0,W,H);
const lineIdx=getCurrentLineIndex(currentTime);
if(lineIdx>=LRC_DATA.length)return;
const lineText=LRC_DATA.text;
let fontSize=baseFontSize;
if(lineText.length>14)fontSize=baseFontSize*0.88;
else if(lineText.length>10)fontSize=baseFontSize*0.98;
const centerY=H/2;
const positions=layoutLine(lineText,fontSize,W/2,centerY);
const startIdx=charData.findIndex(c=>c.lineIndex===lineIdx);
const endIdx=charData.findIndex(c=>c.lineIndex===lineIdx+1);
const lineChars=charData.slice(startIdx,endIdx!==-1?endIdx:charData.length);
const colorBase=lineIdx%colors.length;
for(let i=0;i<positions.length;i++){
const pos=positions;
const charInfo=lineChars;
if(!charInfo)continue;
const charStartTime=charInfo.startTime;
const charDuration=charInfo.duration;
const charEndTime=charStartTime+charDuration;
let progress=0;
if(currentTime>=charEndTime){
progress=1;
}else if(currentTime>=charStartTime){
progress=(currentTime-charStartTime)/charDuration;
progress=Math.min(1,progress*1.2);
}
const color=colors[(colorBase+i)%colors.length];
const scale=1-0.10*Math.abs(i-positions.length/2)/(positions.length/2);
const size=fontSize*scale;
drawChar(mainCtx,pos.char,pos.x,pos.y,size,color,progress);
}
if(lineIdx<LRC_DATA.length-1){
const nextText=LRC_DATA.text;
mainCtx.globalAlpha=0.2;
mainCtx.fillStyle='rgba(255,200,150,0.25)';
mainCtx.font='28px "Long Cang","ZCOOL KuaiLe","华文楷体","KaiTi","楷体","STKaiti",serif';
mainCtx.textAlign='center';
mainCtx.textBaseline='top';
mainCtx.fillText('⋯ '+nextText+' ⋯',W/2,H-22);
mainCtx.globalAlpha=1;
}
}
const timeText=document.getElementById('timeText');
audioPlayer.addEventListener('timeupdate',()=>{
if(!audioPlayer.duration)return;
timeText.textContent=formatTime(audioPlayer.currentTime)+' / '+formatTime(audioPlayer.duration);
});
function formatTime(sec){
if(!sec||isNaN(sec))return '00:00';
const m=Math.floor(sec/60);
const s=Math.floor(sec%60);
return String(m).padStart(2,'0')+':'+String(s).padStart(2,'0');
}
function lyricLoop(){
render(audioPlayer.currentTime);
lyricAnimId=requestAnimationFrame(lyricLoop);
}
lyricLoop();
render(0);
['contextmenu','dragstart','selectstart'].forEach(type=>paBox.addEventListener(type,e=>e.preventDefault()));
msvg.onanimationiteration=()=>{
msvg.style.setProperty('filter',`hue-rotate(${30+Math.random()*180}deg)`);
};
let fs=true,fsTimer,labelTimer;
fullscreen.onclick=()=>{
fs?(fullscreen.innerText='退出全屏',paBox.requestFullscreen()):(fullscreen.innerText='全屏欣赏',document.exitFullscreen());fs=!fs;
};
paBox.addEventListener('mousemove',()=>{
clearTimeout(fsTimer);
fullscreen.style.opacity='1';
fsTimer=setTimeout(()=>{fullscreen.style.opacity='0';},3000);
});
document.addEventListener('visibilitychange',()=>{
if(document.hidden){
if(!audioPlayer.paused)audioPlayer.pause();
}else{
if(audioPlayer.paused&&!audioPlayer.ended)audioPlayer.play().catch(()=>{});
}
});
window.addEventListener('load',()=>{svgTip.textContent="暂停";});
})();
</script>
</body>
</html>

杨帆 发表于 2026-7-3 10:34

代码来自了了子老师分享作品,在此表示感谢{:4_190:}

梦江南 发表于 2026-7-3 11:54

欣赏杨帆带来的精美音画,赞!{:4_187:}

红影 发表于 2026-7-3 14:34

这歌曲像AI歌曲呢。制作真漂亮,欣赏杨帆好歌{:4_187:}
暂停时背景就换了,很奇特,

霜染枫丹 发表于 2026-7-3 19:28

问好扬帆,刚上来就看到好帖,歌声很有味道,特别好听,制作的漂亮,赞佩!!{:4_204:}{:4_190:}

杨帆 发表于 2026-7-3 19:45

本帖最后由 杨帆 于 2026-7-3 20:44 编辑

梦江南 发表于 2026-7-3 11:54
欣赏杨帆带来的精美音画,赞!
问好江南,谢谢鼓励,祝欣赏愉快{:4_204:}

杨帆 发表于 2026-7-3 20:07

本帖最后由 杨帆 于 2026-7-3 20:43 编辑

红影 发表于 2026-7-3 14:34
这歌曲像AI歌曲呢。制作真漂亮,欣赏杨帆好歌
暂停时背景就换了,很奇特,
谢谢影子鼓励,不是AI歌曲呦~祝影子欣赏愉快{:4_187:}

《山歌追上云朵》创作于2026年6月,由董慧作词作曲,原唱央金拉姆,是融合藏地韵味与现代编曲的高原情歌。

杨帆 发表于 2026-7-3 20:11

本帖最后由 杨帆 于 2026-7-3 20:39 编辑

霜染枫丹 发表于 2026-7-3 19:28
问好扬帆,刚上来就看到好帖,歌声很有味道,特别好听,制作的漂亮,赞佩!!
是,这首新歌给人的感觉干净清澈通透,原生态的嗓音直抵人心,谢谢枫丹老师鼓励{:4_204:}

红影 发表于 2026-7-3 21:40

杨帆 发表于 2026-7-3 20:07
谢谢影子鼓励,不是AI歌曲呦~祝影子欣赏愉快

《山歌追上云朵》创作于2026年6月,由董慧作词 ...

感谢杨帆的耐心解说,这首歌很好听呢{:4_187:}

雨季工作室 发表于 2026-7-5 08:46

欣赏杨帆老师精彩分享!

小辣椒 发表于 2026-7-5 22:58

欣赏杨帆的精彩制作,歌曲配的好听{:4_199:}
页: [1]
查看完整版本: 山歌追上云朵