@@ -2,7 +2,7 @@
|
|||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>墨水屏倒计时 Pro - 阵列卡片版</title>
|
<title>墨水屏倒计时 Pro - 动态阵列版</title>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
.header {
|
.header {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 15px 0 10px 20px;
|
padding: 12px 0 8px 15px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
#title-text {
|
#title-text {
|
||||||
@@ -42,68 +42,68 @@
|
|||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 20px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 阵列布局容器 --- */
|
/* --- 阵列布局容器 --- */
|
||||||
.exam-grid-preview {
|
.exam-grid-preview {
|
||||||
padding: 0 15px 15px 15px;
|
padding: 0 12px 12px 12px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
/* 默认两列排布 */
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
grid-auto-rows: 1fr;
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
align-content: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 矩形卡片样式 --- */
|
/* --- 矩形卡片样式 --- */
|
||||||
.preview-card {
|
.preview-card {
|
||||||
border: 2px solid var(--ep-black);
|
border: 2px solid var(--ep-black);
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px;
|
padding: 10px;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: white;
|
background: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 重要考试:红色边框 + 填充效果 */
|
|
||||||
.is-important-card {
|
.is-important-card {
|
||||||
border: 3px solid var(--ep-red) !important;
|
border: 4px solid var(--ep-red) !important;
|
||||||
}
|
}
|
||||||
.is-important-card::after {
|
.is-important-card::after {
|
||||||
content: "★";
|
content: "★";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 2px; right: 4px;
|
top: 4px; right: 6px;
|
||||||
color: var(--ep-red);
|
color: var(--ep-red);
|
||||||
font-size: 14px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-name {
|
.preview-name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--ep-black);
|
color: var(--ep-black);
|
||||||
font-size: 14px;
|
margin-bottom: 5px;
|
||||||
margin-bottom: 4px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.preview-days-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.preview-days-val {
|
.preview-days-val {
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
color: var(--ep-red);
|
color: var(--ep-red);
|
||||||
font-family: 'Arial Black', sans-serif;
|
font-family: 'Arial Black', sans-serif;
|
||||||
font-size: 32px;
|
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
.preview-unit {
|
.preview-unit {
|
||||||
font-size: 12px;
|
font-weight: bold;
|
||||||
color: var(--ep-black);
|
color: var(--ep-black);
|
||||||
margin-left: 2px;
|
margin-left: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- 管理区 --- */
|
/* --- 管理区 --- */
|
||||||
@@ -133,7 +133,7 @@
|
|||||||
<div id="capture-area">
|
<div id="capture-area">
|
||||||
<div class="top-date" id="live-date"></div>
|
<div class="top-date" id="live-date"></div>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 id="title-text">考试倒计时</h1>
|
<h1 id="title-text">倒计时看板</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="exam-grid-preview" id="preview-container"></div>
|
<div class="exam-grid-preview" id="preview-container"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,12 +161,9 @@
|
|||||||
<script>
|
<script>
|
||||||
const getTodayStr = () => new Date().toISOString().split('T')[0];
|
const getTodayStr = () => new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
// 默认数据
|
let exams = JSON.parse(localStorage.getItem('dragExamGridV2')) || [
|
||||||
let exams = JSON.parse(localStorage.getItem('dragExamGridV1')) || [
|
{ name: "期末大考", date: "2026-06-20", imp: true },
|
||||||
{ name: "期末考试", date: "2026-06-20", imp: true },
|
{ name: "驾照预约", date: getTodayStr(), imp: false }
|
||||||
{ name: "英语四级", date: "2026-06-15", imp: false },
|
|
||||||
{ name: "驾照科目一", date: "2026-05-10", imp: false },
|
|
||||||
{ name: "健身计划", date: getTodayStr(), imp: false }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function updateLiveDate() {
|
function updateLiveDate() {
|
||||||
@@ -190,7 +187,7 @@
|
|||||||
renderPreview();
|
renderPreview();
|
||||||
renderManageList();
|
renderManageList();
|
||||||
updateLiveDate();
|
updateLiveDate();
|
||||||
localStorage.setItem('dragExamGridV1', JSON.stringify(exams));
|
localStorage.setItem('dragExamGridV2', JSON.stringify(exams));
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPreview() {
|
function renderPreview() {
|
||||||
@@ -198,13 +195,23 @@
|
|||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
const count = exams.length;
|
const count = exams.length;
|
||||||
|
|
||||||
// 根据数量动态调整列数
|
// 1. 动态确定网格布局
|
||||||
|
let cols = 2;
|
||||||
|
if (count === 1) cols = 1;
|
||||||
|
else if (count > 4) cols = 3;
|
||||||
|
container.style.gridTemplateColumns = `repeat(${cols}, 1fr)`;
|
||||||
|
|
||||||
|
// 2. 根据数量动态计算字体大小 (单位 px)
|
||||||
|
// 逻辑:项目越少,字号越大
|
||||||
|
let baseNameSize, baseNumSize, starSize;
|
||||||
if (count <= 1) {
|
if (count <= 1) {
|
||||||
container.style.gridTemplateColumns = "1fr";
|
baseNameSize = 64; baseNumSize = 80; starSize = 36;
|
||||||
} else if (count > 4) {
|
} else if (count <= 2) {
|
||||||
container.style.gridTemplateColumns = "repeat(3, 1fr)";
|
baseNameSize = 24; baseNumSize = 64; starSize = 20;
|
||||||
|
} else if (count <= 4) {
|
||||||
|
baseNameSize = 18; baseNumSize = 48; starSize = 16;
|
||||||
} else {
|
} else {
|
||||||
container.style.gridTemplateColumns = "repeat(2, 1fr)";
|
baseNameSize = 14; baseNumSize = 32; starSize = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
exams.forEach(item => {
|
exams.forEach(item => {
|
||||||
@@ -212,16 +219,18 @@
|
|||||||
let dayHTML = '';
|
let dayHTML = '';
|
||||||
|
|
||||||
if (days === 0) {
|
if (days === 0) {
|
||||||
dayHTML = `<span class="preview-days-val" style="font-size:24px">今天</span>`;
|
dayHTML = `<span class="preview-days-val" style="font-size:${baseNumSize * 0.6}px">今天</span>`;
|
||||||
} else if (days < 0) {
|
} else if (days < 0) {
|
||||||
dayHTML = `<span class="preview-days-val" style="font-size:24px; color:#666">已过</span>`;
|
dayHTML = `<span class="preview-days-val" style="font-size:${baseNumSize * 0.6}px; color:#666">已过</span>`;
|
||||||
} else {
|
} else {
|
||||||
dayHTML = `<span class="preview-days-val">${days}</span><span class="preview-unit">天</span>`;
|
dayHTML = `<span class="preview-days-val" style="font-size:${baseNumSize}px">${days}</span>
|
||||||
|
<span class="preview-unit" style="font-size:${baseNumSize * 0.3}px">天</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
container.innerHTML += `
|
container.innerHTML += `
|
||||||
<div class="preview-card ${item.imp ? 'is-important-card' : ''}">
|
<div class="preview-card ${item.imp ? 'is-important-card' : ''}">
|
||||||
<div class="preview-name">${item.name}</div>
|
${item.imp ? `<style>.is-important-card::after{font-size:${starSize}px}</style>` : ''}
|
||||||
|
<div class="preview-name" style="font-size:${baseNameSize}px">${item.name}</div>
|
||||||
<div class="preview-days-info">
|
<div class="preview-days-info">
|
||||||
${dayHTML}
|
${dayHTML}
|
||||||
</div>
|
</div>
|
||||||
@@ -247,13 +256,11 @@
|
|||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateItem(index, key, value) {
|
function updateItem(index, key, value) {
|
||||||
exams[index][key] = value;
|
exams[index][key] = value;
|
||||||
renderPreview();
|
renderPreview();
|
||||||
localStorage.setItem('dragExamGridV1', JSON.stringify(exams));
|
localStorage.setItem('dragExamGridV2', JSON.stringify(exams));
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveOrder() {
|
function saveOrder() {
|
||||||
const newExams = [];
|
const newExams = [];
|
||||||
document.querySelectorAll('.manage-item').forEach(el => {
|
document.querySelectorAll('.manage-item').forEach(el => {
|
||||||
@@ -262,7 +269,6 @@
|
|||||||
exams = newExams;
|
exams = newExams;
|
||||||
renderAll();
|
renderAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addItem() {
|
function addItem() {
|
||||||
const n = document.getElementById('name-in').value;
|
const n = document.getElementById('name-in').value;
|
||||||
const d = document.getElementById('date-in').value;
|
const d = document.getElementById('date-in').value;
|
||||||
@@ -273,39 +279,29 @@
|
|||||||
document.getElementById('name-in').value = '';
|
document.getElementById('name-in').value = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeItem(index) {
|
function removeItem(index) {
|
||||||
exams.splice(index, 1);
|
exams.splice(index, 1);
|
||||||
renderAll();
|
renderAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadImage() {
|
function downloadImage() {
|
||||||
html2canvas(document.getElementById('capture-area'), {
|
html2canvas(document.getElementById('capture-area'), { width: 400, height: 300, scale: 2 })
|
||||||
width: 400,
|
|
||||||
height: 300,
|
|
||||||
scale: 2,
|
|
||||||
backgroundColor: "#ffffff"
|
|
||||||
})
|
|
||||||
.then(canvas => {
|
.then(canvas => {
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.download = `E-Paper_Grid_${getTodayStr()}.png`;
|
link.download = `Grid_Countdown.png`;
|
||||||
link.href = canvas.toDataURL("image/png");
|
link.href = canvas.toDataURL();
|
||||||
link.click();
|
link.click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportJSON() {
|
function exportJSON() {
|
||||||
const blob = new Blob([JSON.stringify(exams)], {type: 'application/json'});
|
const blob = new Blob([JSON.stringify(exams)], {type: 'application/json'});
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = URL.createObjectURL(blob); a.download = 'exams_config.json'; a.click();
|
a.href = URL.createObjectURL(blob); a.download = 'exams.json'; a.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function importJSON(input) {
|
function importJSON(input) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = e => { exams = JSON.parse(e.target.result); renderAll(); };
|
reader.onload = e => { exams = JSON.parse(e.target.result); renderAll(); };
|
||||||
reader.readAsText(input.files[0]);
|
reader.readAsText(input.files[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAll();
|
renderAll();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
+248
@@ -0,0 +1,248 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>简介生成器</title>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--ink-red: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Source Han Sans CN", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||||
|
background-color: #f4f4f7;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 墨水屏预览区 400x300 */
|
||||||
|
#screen-wrap {
|
||||||
|
padding: 10px;
|
||||||
|
background: #333;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#screen-canvas {
|
||||||
|
width: 400px;
|
||||||
|
height: 300px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
display: flex;
|
||||||
|
padding: 25px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 布局样式 */
|
||||||
|
.info-side {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
border-right: 2px solid var(--ink-red);
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#disp-name {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 900;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: var(--ink-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
#disp-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
padding: 2px 8px;
|
||||||
|
display: inline-block;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-side {
|
||||||
|
width: 130px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qr-img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
padding: 5px;
|
||||||
|
border: 2px solid #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#disp-qr-label {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--ink-red);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 编辑面板 */
|
||||||
|
.editor-panel {
|
||||||
|
background: white;
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 420px;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 6px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 14px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus { border-color: var(--ink-red); }
|
||||||
|
|
||||||
|
.btn-download {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: var(--ink-red);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 10px;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-download:hover { opacity: 0.9; }
|
||||||
|
|
||||||
|
.hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="screen-wrap">
|
||||||
|
<div id="screen-canvas">
|
||||||
|
<div class="info-side">
|
||||||
|
<div id="disp-name">张三</div>
|
||||||
|
<div id="disp-title">全栈开发工程师</div>
|
||||||
|
<div class="details" id="disp-details">
|
||||||
|
📍 坐标:北京 · 朝阳<br>
|
||||||
|
📧 邮箱:zhangsan@dev.com<br>
|
||||||
|
🔗 博客:blog.zhangsan.me
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="qr-side">
|
||||||
|
<img id="qr-img" src="https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=https://github.com" alt="QR" crossOrigin="anonymous">
|
||||||
|
<div id="disp-qr-label">扫码获取简历</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="editor-panel">
|
||||||
|
<div class="input-group">
|
||||||
|
<label>姓名 (红色)</label>
|
||||||
|
<input type="text" id="in-name" value="张三" oninput="update()">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>职业标签 (黑底白字)</label>
|
||||||
|
<input type="text" id="in-title" value="全栈开发工程师" oninput="update()">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>个人简介 (支持换行)</label>
|
||||||
|
<textarea id="in-details" rows="3" oninput="update()">📍 坐标:北京 · 朝阳 📧 邮箱:zhangsan@dev.com 🔗 博客:blog.zhangsan.me</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>二维码链接</label>
|
||||||
|
<input type="text" id="in-qr-data" value="https://github.com" onchange="update()">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>二维码下方文案 (红色)</label>
|
||||||
|
<input type="text" id="in-qr-label" value="扫码获取简历" oninput="update()">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn-download" onclick="downloadImage()">保存图片到本地</button>
|
||||||
|
|
||||||
|
<div class="hint">
|
||||||
|
生成的图片尺寸固定为 400x300,完美适配 4.2" 墨水屏。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 更新预览内容
|
||||||
|
function update() {
|
||||||
|
document.getElementById('disp-name').innerText = document.getElementById('in-name').value;
|
||||||
|
document.getElementById('disp-title').innerText = document.getElementById('in-title').value;
|
||||||
|
|
||||||
|
const details = document.getElementById('in-details').value;
|
||||||
|
document.getElementById('disp-details').innerHTML = details.replace(/\n/g, '<br>');
|
||||||
|
|
||||||
|
document.getElementById('disp-qr-label').innerText = document.getElementById('in-qr-label').value;
|
||||||
|
|
||||||
|
// 二维码更新
|
||||||
|
const qrData = encodeURIComponent(document.getElementById('in-qr-data').value);
|
||||||
|
// 注意:qrserver支持跨域,html2canvas 才能捕获它
|
||||||
|
document.getElementById('qr-img').src = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${qrData}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载图片功能
|
||||||
|
function downloadImage() {
|
||||||
|
const screen = document.getElementById('screen-canvas');
|
||||||
|
|
||||||
|
// 使用 html2canvas 捕捉指定节点
|
||||||
|
html2canvas(screen, {
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
scale: 1, // 保持 1:1 像素
|
||||||
|
useCORS: true, // 允许加载跨域二维码图片
|
||||||
|
backgroundColor: "#ffffff"
|
||||||
|
}).then(canvas => {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.download = `eink_profile_${Date.now()}.png`;
|
||||||
|
link.href = canvas.toDataURL("image/png");
|
||||||
|
link.click();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始运行一次
|
||||||
|
window.onload = update;
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>4.2寸墨水屏看板 (红色提醒版)</title>
|
||||||
|
<style>
|
||||||
|
:root { --ink-red: #ff0000; --ink-black: #000000; }
|
||||||
|
body { font-family: sans-serif; background: #f0f2f5; display: flex; flex-direction: column; align-items: center; padding: 20px; }
|
||||||
|
.container { display: flex; gap: 20px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
|
||||||
|
.editor { width: 300px; }
|
||||||
|
.input-box { display: flex; gap: 5px; margin-bottom: 15px; }
|
||||||
|
input { flex: 1; padding: 8px; border: 1px solid #ddd; }
|
||||||
|
.task-item { display: flex; align-items: center; padding: 8px; border-bottom: 1px solid #eee; font-size: 14px; }
|
||||||
|
canvas { border: 1px solid #000; background: #fff; width: 400px; height: 300px; }
|
||||||
|
.btn-dl { width: 100%; margin-top: 15px; padding: 10px; background: var(--ink-red); color: white; border: none; cursor: pointer; font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>三色墨水屏待办生成器</h2>
|
||||||
|
<p style="color: #666;">提示:红色代表<strong>未完成</strong>(急需处理),黑色代表<strong>已完成</strong>。</p>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="editor">
|
||||||
|
<div class="input-box">
|
||||||
|
<input type="text" id="taskInput" placeholder="添加任务...">
|
||||||
|
<button onclick="addTask()">添加</button>
|
||||||
|
</div>
|
||||||
|
<div id="listUI"></div>
|
||||||
|
<button class="btn-dl" onclick="download()">下载 400x300 图片</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<canvas id="canvas" width="400" height="300"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
let tasks = [
|
||||||
|
{ text: "Class 612 网站数据库备份", done: false },
|
||||||
|
{ text: "Surveying 测量平差作业", done: false },
|
||||||
|
{ text: "已完成的演示任务", done: true }
|
||||||
|
];
|
||||||
|
|
||||||
|
function addTask() {
|
||||||
|
const val = document.getElementById('taskInput').value;
|
||||||
|
if(val) { tasks.push({text: val, done: false}); render(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
// UI 渲染
|
||||||
|
const listUI = document.getElementById('listUI');
|
||||||
|
listUI.innerHTML = '';
|
||||||
|
tasks.forEach((t, i) => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'task-item';
|
||||||
|
div.innerHTML = `<input type="checkbox" ${t.done?'checked':''} onchange="tasks[${i}].done=!tasks[${i}].done;render()">
|
||||||
|
<span style="flex:1; margin-left:8px; ${t.done?'text-decoration:line-through;color:#999':''}">${t.text}</span>
|
||||||
|
<button onclick="tasks.splice(${i},1);render()">×</button>`;
|
||||||
|
listUI.appendChild(div);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Canvas 绘图
|
||||||
|
const ctx = document.getElementById('canvas').getContext('2d');
|
||||||
|
ctx.fillStyle = "#fff"; ctx.fillRect(0,0,400,300);
|
||||||
|
|
||||||
|
// 标题栏 (黑色)
|
||||||
|
ctx.fillStyle = "#000"; ctx.font = "bold 24px 'Microsoft YaHei'";
|
||||||
|
ctx.fillText("Focus Tasks", 20, 45);
|
||||||
|
ctx.fillRect(20, 55, 360, 2); // 黑线下划线
|
||||||
|
|
||||||
|
// 绘制列表
|
||||||
|
tasks.forEach((t, i) => {
|
||||||
|
const y = 90 + i * 38;
|
||||||
|
if(y > 270) return;
|
||||||
|
|
||||||
|
if(!t.done) {
|
||||||
|
// --- 未完成任务:红色强调 ---
|
||||||
|
ctx.strokeStyle = "#f00"; ctx.fillStyle = "#f00"; ctx.lineWidth = 2;
|
||||||
|
// 空心框
|
||||||
|
ctx.strokeRect(20, y - 16, 18, 18);
|
||||||
|
// 粗体文字
|
||||||
|
ctx.font = "bold 19px 'Microsoft YaHei'";
|
||||||
|
ctx.fillText(t.text, 50, y);
|
||||||
|
} else {
|
||||||
|
// --- 已完成任务:黑色弱化 ---
|
||||||
|
ctx.strokeStyle = "#000"; ctx.fillStyle = "#000"; ctx.lineWidth = 1;
|
||||||
|
// 打钩框
|
||||||
|
ctx.strokeRect(20, y - 16, 18, 18);
|
||||||
|
ctx.beginPath(); ctx.moveTo(22, y-8); ctx.lineTo(28, y); ctx.lineTo(36, y-12); ctx.stroke();
|
||||||
|
// 普通文字 + 删除线
|
||||||
|
ctx.font = "17px 'Microsoft YaHei'";
|
||||||
|
ctx.fillText(t.text, 50, y);
|
||||||
|
ctx.beginPath(); ctx.moveTo(50, y-6); ctx.lineTo(380, y-6); ctx.stroke();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 底部提示 (黑色)
|
||||||
|
ctx.fillStyle = "#000"; ctx.font = "12px monospace";
|
||||||
|
ctx.fillText(`Update: ${new Date().toLocaleTimeString()}`, 20, 290);
|
||||||
|
ctx.fillText(`Pending: ${tasks.filter(x=>!x.done).length}`, 310, 290);
|
||||||
|
}
|
||||||
|
|
||||||
|
function download() {
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.download = 'eink_todo.png';
|
||||||
|
link.href = document.getElementById('canvas').toDataURL();
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user