马氏HTML编辑器
本帖最后由 亚伦影音工作室 于 2025-5-31 17:17 编辑 <br /><br /><style>iframe { position: relative; width: 100%; height: 100%; border: none; outline: none; box-sizing: border-box; margin: 0; }
.wrap{ left:calc(50% - 81px); transform:translateX(-50%);width:1400px; height:700px; background:linear-gradient(to right,#eee 59px,#aaa 60px,#fff 0); border:1px solid gray; border-radius:4px; box-shadow:2px 2px 6px gray; z-index: 1; position:relative; margin: 30px 0; }
@media screen and (max-width: 1280px) { .wrap { width: 90vw; height: 75vh; font-size: 12px; } }
.wrap:hover{ border-color: black; }
.wrap:has(#editor:focus) { border-width:2px; }
.same{ position: absolute; top:10px; box-sizing: border-box; padding: 8px; border:none; border-radius: inherit; white-space: pre-wrap; word-break: break-all; font: normal 16px/24px Consolas,'Courier New','Andale Mono',monospace; tab-size: 4; overflow: auto; }
#editor{ right: 0; width: calc(100% - 60px); height: calc(100% - 20px); resize: none; outline: none; background: #fefefe; scroll-padding: 8px;}
#line-number{ position: absolute; width: 100%; height: calc(100% - 20px); color: gray; user-select: none; }
#line-number > span{cursor:pointer;}
#line-number > span:hover{ font-weight: bold; color: red; }
#mnbox{ min-height: 0; visibility: hidden; }
#showBox { position: fixed; top: 0; right: 0; bottom: 0; left: 0; background: #fff; display: none; padding: 0; overflow: hidden; z-index: 10000; margin: 0; }
#showBox::after { position: absolute; content: '关闭预览'; bottom: 10px; left: calc(50% - 40px); padding: 0 4px; width: 80px; height: 30px; line-height: 30px; text-align: center; border: 1px solid #efe; border-radius: 6px; background: #eee; font-size: 14px; box-shadow: 2px 2px 6px rgba(0,0,0,.25); cursor: pointer; }
#btnPrev { padding: 2px 8px; cursor: pointer; border: 1px solid #efe; border-radius: 4px; box-shadow:2px 2px 6px rgba(0,0,0,.25); font-size: 14px; }
#btnPrev:hover { color: red; }
</style>
<h2>Web代码简易编辑器</h2>
<div class="wrap">
<div id="mnbox" class="same"></div>
<div id="line-number" class="same"></div>
<textarea id="editor" class="same" placeholder="输入CSS+HTML+JS代码..."></textarea>
</div>
<p><button type="button" id="btnPrev" title="预览效果">运行代码</button></p>
<div id="showBox" title="点击关闭"></div>
<script>
let isSyncing = false;
const lnum = document.getElementById('line-number');
const editor = document.getElementById('editor');
const mnbox = document.getElementById('mnbox');
const btnPrev = document.getElementById('btnPrev');
const selectLine = (num) => {
let mpos, tstr = '';
let ar = editor.value.split('\n');
for (j = 0; j < num; j ++) {
tstr += ar + 2;
}
mpos = tstr.length;
editor.setSelectionRange(mpos, mpos + ar.length);
editor.focus();
};
const addLineNum = () => {
const lines = editor.value.split('\n');
let str = '';
lines.forEach((line, key) => {
let br = '<br>'.repeat(calcSolftLines(line) || 1);
let idx = (key + 1).toString().padStart(4,' ');
str += `<span onclick="selectLine(${key})">${idx}</span>${br}`;
});
lnum.innerHTML = str;
};
const calcSolftLines = (text) => {
const cssdata = window.getComputedStyle(editor);
mnbox.style.width = editor.offsetWidth + 'px';
mnbox.style.overflowY = editor.scrollTop > 0 ? 'scroll' : 'hidden';
mnbox.innerText = text;
const lineheight = parseFloat(cssdata.getPropertyValue('line-height'));
const padding = parseFloat(cssdata.getPropertyValue('padding'));
return Math.floor((mnbox.offsetHeight - padding * 2) / lineheight);
};
const getCurrentLineIndent = () => {
const cursorPos = editor.selectionStart;
const content = editor.value;
let lineStart = content.lastIndexOf('\n', cursorPos - 1) + 1;
const line = content.slice(lineStart, cursorPos);
return line.match(/^[\t ]*/)?. || '';
};
const insertTextAtCursor = (text) => {
const start = editor.selectionStart;
const end = editor.selectionEnd;
const content = editor.value;
editor.value = content.slice(0, start) + text + content.slice(end);
editor.selectionStart = editor.selectionEnd = start + text.length;
};
const preView = (htmlCode, targetBox) => {
if (targetBox.innerHTML) return;
console.log('Yes');
window.localStorage.setItem('ed1', htmlCode);
const iframe = document.createElement('iframe');
htmlCode = htmlCode + '\n<style>body {margin: 0; }</style>\n';
iframe.srcdoc = htmlCode;
targetBox.appendChild(iframe);
targetBox.style.display = 'block';
targetBox.onclick = () => {
targetBox.innerHTML = '';
targetBox.style.display = 'none';
}
};
btnPrev.onclick = () => preView(editor.value, showBox);
editor.onkeydown = (e) => {
if (e.key === 'Tab') {
e.preventDefault();
insertTextAtCursor('\t');
}
if (e.key === 'Enter') {
e.preventDefault();
const indent = getCurrentLineIndent();
insertTextAtCursor('\n' + indent);
addLineNum();
}
};
const bindEventLnum = () => {
removeEventEditor();
lnum.addEventListener('scroll', lnumScroll);
};
const bindEventEditor = () => {
removeEventLnum();
editor.addEventListener('scroll', editorScroll);
};
const lnumScroll = () => {
if (!isSyncing) {
isSyncing = true;
editor.scrollTop = lnum.scrollTop;
isSyncing = false;
}
};
const editorScroll = () => {
if (!isSyncing) {
isSyncing = true;
lnum.scrollTop = editor.scrollTop;
isSyncing = false;
}
};
lnum.addEventListener('mouseover', bindEventLnum);
editor.addEventListener('mouseover', bindEventEditor);
lnum.addEventListener('touchstart', bindEventLnum, { passive: false });
editor.addEventListener('touchstart', bindEventEditor, { passive: false });
const removeEventLnum = () => lnum.removeEventListener('scroll', lnumScroll);
const removeEventEditor = () => editor.removeEventListener('scroll', editorScroll);
editor.oninput = () => addLineNum();
window.onresize = () => addLineNum();
editor.onmouseenter = () => editor.focus();
editor.value = window.localStorage.getItem('ed1');
editor.setSelectionRange(0, 0);
addLineNum();
</script> 好,用起来很方便{:4_191:}
儿童节快乐
本帖最后由 亚伦影音工作室 于 2025-5-31 20:00 编辑 <br /><br /><style>#papa { margin: 0 0 0 calc(50% - 718px); width: 1300px; height:720px; background: #000080; overflow: hidden; display: grid; place-items: center; box-shadow: 0px 0px 0px gray; position: relative; z-index: 1; --state: paused; }
#dh{ width: 100%; height: 100%; position: absolute;
z-index: 1;background:url(https://pic1.imgdb.cn/item/683ae68458cb8da5c81eb3bd.jpg)no-repeat center/cover;
top:0px; left:0px;
animation: rod 4s linear infinite var(--state);}
@keyframes rod{0% {opacity: 1;filter:hue-rotate(10deg)}
50% {opacity: 0.3;}100% {opacity: 1;}
}
li-zi { position: absolute; width: 220px; height: 1px; border-radius: 100% 20%;background: tan;animation: imov 2s infinite;z-index: 2;}
@keyframes moving {
to{ opacity: 1; transform: translate(var(--x0),var(--y0)) rotate(var(--deg)); filter:hue-rotate(360deg);}
from{ opacity: 0; transform: translate(0,0) rotate(var(--deg)); }
}
@keyframes rotating { to { transform: rotate(360deg); } }
.mplayer { position: absolute; width: 300px; height: fit-content; display: flex; flex-direction: column; align-items: center; gap: 10px; color: #000; margin: auto; top: 90%; left:64%;z-index: 20; }
.mplayer::before { position: absolute; content: attr(data-time); width: 100%; text-align-last: justify; pointer-events: none;color: #000; }
.btnPlay { width: 20px; height: 20px; cursor: pointer; position: relative; }
.btnPlay::after { position: absolute; content: ''; width: 100%; height: 100%; background: red; clip-path: var(--clip); }
.progress { --prg: 0%; position: relative; width: 100%; height: 20px; display: grid; place-items: center start; background: linear-gradient(90deg, red var(--prg), gray var(--prg), gray 0) no-repeat center/100% 2px; padding: 0; margin: 0; }
.thumb { position: absolute; left: calc(var(--prg) - 10px); width: 10px; height: 10px; background: red; border: 1px solid #FF0000; border-radius: 50%; cursor: pointer; box-sizing: border-box; }
.play { --clip: polygon(10% 0,100% 50%,10% 100%); }
.pause { --clip: polygon(35% 0,15% 0,15% 100%, 35% 100%,35% 0,75% 0,75% 100%,55% 100%,55% 0); }
.lrc {position:absolute;width: 600px;
height: 450px;z-index: 3; left:2%;
border: 0px solid white;
overflow: hidden;margin: 6px;
}
.lrc #ul {width: 100%;
padding: 0;list-style: none;transition: 0.3s all ease;
margin: 0}
.lrc #ul li {
font: normal 22px 'FZYaoti', sans-serif;
color: #000;
font-weight: normal;
transition: .3s all ease;
list-style-type: none;
text-align: center;display: block;
width: 100%;
margin: 0 auto;
height: 50px;
line-height: 35px;
}
.lrc #ulli.active{ font-size: 38px;
color: #ff0000;
text-align: center; }
#fullscreen {border-radius: 4px;position: absolute;background:#0000 ;
color:#fff;box-shadow:0px 0px 0px 1px #fff;z-index: 20;
padding: 4px 10px;
font-size: 12px;
border: none;
cursor: pointer;margin: 8px 5px;left: 85%;top: 4%;
}
</style>
<div id="papa">
<span id="fullscreen" title="屏展模式">全屏欣赏</span>
<div id="dh" ></div>
<div class="mplayer" data-time="00:00 00:00">
<div class="btnPlay play"></div>
<div class="progress">
<div class="thumb"></div>
</div>
</div>
<div class="lrc">
<ul id="ul">
</ul>
</div>
<audio id="audio"autoplay loop>
<source src="https://s2.ananas.chaoxing.com/sv-w8/audio/00/44/a9/8ccdc45a29f198c0de7b0878cdc254ce/audio.mp3" type="audio/mpeg">
</audio>
</div>
<script>
//获取需要操作的元素标识
const mplayer = document.querySelector('.mplayer');
const btnPlay = document.querySelector('.btnPlay');
const progress = document.querySelector('.progress');
const thumb = document.querySelector('.thumb');
const audio = document.querySelector('audio');
// 拖曳操作状态(初始值为假)
let isDraggable = false;
//时间格式化工具函数 :秒转分秒 mm:ss 格式
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
};
// 联动函数 mState :处理按钮形状
const mState = () => {
btnPlay.className = `btnPlay ${['pause', 'play'][+audio.paused]}`;
};
// 获取设备指针所在点在进度条上的距离(百分比)
const getPercent = (e) => {
const rect = progress.getBoundingClientRect();
const left = rect.left;
const width = rect.width;
let x = e.clientX ?? e.touches?.?.clientX ?? e.changedTouches?.?.clientX;
x = Math.min(width, Math.max(0, x - left));
return x / width * 100;
};
// 滑块鼠标按下、触屏设备手指或触笔按下
thumb.onmousedown = thumb.ontouchstart = (e) => {
isDraggable = true; // 拖曳状态进行中
e.preventDefault(); // 阻止默认行为
};
// 文档指针松开、触屏设备手指或触笔弹开
document.onmouseup = document.ontouchend = (e) => {
// 松开时若拖曳状态为真,驱动 audio 改变播放进度
if (isDraggable) audio.currentTime = `${getPercent(e) * audio.duration / 100}`;
isDraggable = false; //然后拖曳状态为假
};
// 文档上指针或手指、触笔移动时
document.onmousemove = document.ontouchmove = (e) => {
if (!isDraggable) return; // 若不是拖曳状态则忽略之
// 反之,若处于拖曳状态,给CSS变量 --prg 赋值
progress.style.setProperty('--prg', `${getPercent(e)}%`);
// 给时间文本信息即mplayer伪元素 attr(data-time) 函数赋值
mplayer.dataset.time = `${formatTime(audio.duration * getPercent(e) / 100)} ${formatTime(audio.duration)}`;
};
// 进度条点击事件
progress.onclick = (e) => audio.currentTime = `${getPercent(e) * audio.duration / 100}`;
// 音频标签开始播放和暂停时执行联动函数
audio.onplaying = audio.onpause = () => mState();
// 音频时间更新事件 :驱动文本时间信息及进度条进度变更
audio.ontimeupdate = () => {
if (isDraggable) return; // 拖曳操作发生时忽略
mplayer.dataset.time = `${formatTime(audio.currentTime)} ${formatTime(audio.duration)}`;
progress.style.setProperty('--prg', `${audio.currentTime / audio.duration * 100}%`);
};
// 按钮单击 :播放、暂停状态切换
btnPlay.onclick = () => audio.paused ? audio.play() : audio.pause();
let fs = true;
fullscreen.onclick = () => {
if (fs) {
fullscreen.innerText = '退出全屏';
papa.requestFullscreen();
} else {
fullscreen.innerText = '全屏欣赏';
document.exitFullscreen();
}
fs = !fs;
};
</script>
<script>
var lrc = `隐形的翅膀 - 左溢&张韶涵
X:每一次
都在徘徊孤单中坚强
每一次
就算很受伤
也不闪泪光
我知道
我一直有双隐形的翅膀
带我飞
给我希望
张:不去想
他们拥有美丽的太阳
我看见
每天的夕阳
也会有变化
我知道
我一直有双隐形的翅膀
带我飞
给我希望
合:我终于
看到
所有梦想都开花
追逐的年轻
歌声多嘹亮
我终于
翱翔
用心凝望不害怕
哪里会有风
就飞多远吧
不去想
他们拥有美丽的太阳
我看见
每天的夕阳
也会有变化
我知道
我一直有双隐形的翅膀
带我飞
给我希望
X:我终于
看到
所有梦想都开花
追逐的年轻
歌声多嘹亮
张:我终于
翱翔
用心凝望不害怕
哪里会有风
就飞多远吧
合:隐形的翅膀
让梦恒久比天长
留一个 愿望
让自己想象
亚伦影音工作室
`;
// 最开始获取到的歌词列表是字符串类型(不好操作)
let lrcArr = lrc.split('\n');
// 接收修正后的歌词数组
let result = [];
// 获取所要用到的dom列表
doms = {
audio: document.querySelector("#audio"),
ul: document.querySelector("#ul"),
container: document.querySelector(".lrc")
}
// 将歌词数组转成由对象组成的数组,对象有time和word两个属性(为了方便操作)
for (let i = 0; i < lrcArr.length; i++) {
var lrcData = lrcArr.split(']');
var lrcTime = lrcData.substring(1);
var obj = {
time: parseTime(lrcTime),
word: lrcData
}
result.push(obj);
}
// 将tiem转换为秒的形式
function parseTime(lrcTime) {
lrcTimeArr = lrcTime.split(":")
return +lrcTimeArr * 60 + +lrcTimeArr;
}
// 获取当前播放到的歌词的下标
function getIndex() {
let Time = doms.audio.currentTime;
for (let i = 0; i < result.length; i++) {
if (result.time > Time) {
return i - 1;
}
}
}
// 创建歌词列表
function createElements() {
let frag = document.createDocumentFragment(); // 文档片段
for (let i = 0; i < result.length; i++) {
let li = document.createElement("li");
li.innerText = result.word;
frag.appendChild(li);
}
doms.ul.appendChild(frag);
}
createElements();
// 获取显示窗口的可视高度
let containerHeight = doms.container.clientHeight;
// 获取歌词列表的可视高度
let liHeight = doms.ul.children.clientHeight;
// 设置最大最小偏移量,防止显示效果不佳
let minOffset = 0;
let maxOffset = doms.ul.clientHeight - containerHeight;
// 控制歌词滚动移动的函数
function setOffset() {
let index = getIndex();
// 计算滚动距离
let offset = liHeight * index - containerHeight / 2 + liHeight / 2;
if (offset < minOffset) {
offset = minOffset;
};
if (offset > maxOffset) {
offset = maxOffset;
};
// 滚动
doms.ul.style.transform = `translateY(-${offset}px)`;
// 清除之前的active
let li = doms.ul.querySelector(".active")
if (li) {
li.classList.remove("active");
}
// 为当前所唱到的歌词添加active
li = doms.ul.children;
if (li) {
li.classList.add("active");
}
};
// 当audio的播放时间更新时,触发该事件
doms.audio.addEventListener("timeupdate", setOffset);
</script>
<script>
(function() {
let all = 160;
for(let i = 0; i < all; i++) {
let movBall = document.createElement('li-zi');
let hudu = Math.PI / 180 * 360 / all * i;
let xx = 800 * Math.cos(hudu), yy = 800 * Math.sin(hudu);
movBall.style.cssText += `
--x0: ${xx}px;
--y0: ${yy}px;
--deg: ${360 / all * i}deg;
background: #${Math.random().toString(16).substr(-6)};
animation: moving ${Math.random() * 2 + 2}s -${Math.random() * 2}s infinite var(--state);
`;
papa.prepend(movBall);
}
let mState = () => papa.style.setProperty('--state', audio.paused ? 'paused' : 'running');
audio.addEventListener('playing',mState,false);
audio.addEventListener('pause',mState,false);
})();
</script>
亚伦影音工作室 发表于 2025-5-31 19:33
本帖最后由 亚伦影音工作室 于 2025-5-31 20:00 编辑
#papa { margin: 0 0 0 calc(50% - 718px);...
漂亮!祝亚伦老师童心不泯、青春永驻{:4_191:} 亚伦影音工作室 发表于 2025-5-31 19:33
本帖最后由 亚伦影音工作室 于 2025-5-31 20:00 编辑
#papa { margin: 0 0 0 calc(50% - 718px);...
谢谢亚伦老师的美好祝福,祝您六一儿童节快乐{:4_187:}
页:
[1]