commit 924bca9f9878cdd306792ce794691a8549da63e4 Author: dafe Date: Mon Nov 2 14:57:08 2026 +0800 feat: 沙香科技官网 v2.2 基线(深色科技风/双语/局域网+公网双部署) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20200fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# 工作日志不入库(用户要求 PROGRESS 不提交) +docs/PROGRESS.md + +# 源资料:17MB 的 PPT 原件(素材已提取到 assets,需要时从原文件恢复) +产品手册.pptx +.~产品手册.pptx + +# macOS +.DS_Store + +# 生成物备份(原图原件,可从 PPT / 用户素材重新生成) +assets/img/qrcode-orig.png diff --git a/assets/brand/contact-avatar.png b/assets/brand/contact-avatar.png new file mode 100644 index 0000000..46e5242 Binary files /dev/null and b/assets/brand/contact-avatar.png differ diff --git a/assets/css/style.css b/assets/css/style.css new file mode 100644 index 0000000..35a8dbd --- /dev/null +++ b/assets/css/style.css @@ -0,0 +1,646 @@ +/* ============================================================ + 沙香科技官网 v2 — 深色科技风 + 设计语言(2026 范式):极光渐变 + 噪点 + Bento 网格 + 发光描边 + 品牌色取自 logo:深蓝底 + 红;辅助青蓝呼应产品摄影冷色调 + ============================================================ */ + +:root { + --bg-0: #04070f; + --bg-1: #070b16; + --bg-2: rgba(148, 178, 255, 0.04); + --line: rgba(148, 178, 255, 0.14); + --text: #e9eef8; + --muted: #93a0b8; + --brand: #ff3d5a; + --cyan: #4cc3ff; + --grad: linear-gradient(96deg, var(--cyan), var(--brand)); + --radius: 16px; + --container: 1180px; +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +html { scroll-behavior: smooth; } + +body { + background: var(--bg-0); + color: var(--text); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", "Microsoft YaHei", sans-serif; + line-height: 1.7; + -webkit-font-smoothing: antialiased; + overflow-x: hidden; +} + +/* 噪点颗粒:消除渐变色带、增加质感(subtle grain) */ +body::after { + content: ""; + position: fixed; + inset: 0; + z-index: 2000; + pointer-events: none; + opacity: 0.04; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); +} + +img { display: block; max-width: 100%; } +a { color: inherit; text-decoration: none; } +ul { list-style: none; } + +.container { max-width: var(--container); margin: 0 auto; padding: 0 24px; } + +.grad-text { + background: var(--grad); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} + +/* ============================================================ + 导航 + ============================================================ */ +.site-header { + position: fixed; + inset: 0 0 auto 0; + z-index: 100; + transition: background 0.3s, backdrop-filter 0.3s, border-color 0.3s; + border-bottom: 1px solid transparent; +} +.site-header.scrolled { + background: rgba(4, 7, 15, 0.78); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border-color: var(--line); +} +.nav-wrap { display: flex; align-items: center; gap: 20px; height: 68px; } +.brand { flex-shrink: 0; } +/* 反白版 logo(tools/make_logo_light.py):黑字提亮为白、彩色保留色相,深底页面直接融合 */ +.brand-logo { height: 38px; width: auto; } +.nav { display: flex; align-items: center; gap: 26px; margin-left: auto; font-size: 14.5px; } +.nav a { color: var(--muted); transition: color 0.2s; } +.nav a:hover { color: var(--text); } +.nav-cta { + padding: 7px 18px; + border: 1px solid var(--line); + border-radius: 999px; + color: var(--text) !important; + transition: border-color 0.2s, box-shadow 0.2s; +} +.nav-cta:hover { border-color: var(--brand); box-shadow: 0 0 18px rgba(255, 61, 90, 0.25); } + +.lang-toggle { + background: none; + border: 1px solid var(--line); + color: var(--muted); + border-radius: 999px; + width: 44px; + height: 32px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: color 0.2s, border-color 0.2s; + flex-shrink: 0; +} +.lang-toggle:hover { color: var(--text); border-color: var(--cyan); } + +.menu-toggle { display: none; flex-direction: column; gap: 5px; background: none; border: none; cursor: pointer; padding: 6px; } +.menu-toggle span { width: 22px; height: 2px; background: var(--text); border-radius: 2px; transition: transform 0.25s, opacity 0.25s; } +.menu-toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); } +.menu-toggle.open span:nth-child(2) { opacity: 0; } +.menu-toggle.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } + +/* ============================================================ + Hero — 极光 + 网格(无照片,对比度完全可控) + ============================================================ */ +.hero { position: relative; min-height: 92vh; display: flex; align-items: center; overflow: hidden; } + +/* 三团极光:青 / 品红 / 深蓝,各自缓慢漂移呼吸 */ +.aurora { position: absolute; inset: -20%; filter: blur(90px); } +.aurora i { position: absolute; border-radius: 50%; opacity: 0.5; } +.aurora i:nth-child(1) { + width: 44vw; height: 44vw; left: -6%; top: -12%; + background: radial-gradient(circle, rgba(76, 195, 255, 0.55), transparent 65%); + animation: drift1 14s ease-in-out infinite alternate; +} +.aurora i:nth-child(2) { + width: 40vw; height: 40vw; right: -8%; top: 8%; + background: radial-gradient(circle, rgba(255, 61, 90, 0.38), transparent 65%); + animation: drift2 18s ease-in-out infinite alternate; +} +.aurora i:nth-child(3) { + width: 46vw; height: 46vw; left: 28%; bottom: -26%; + background: radial-gradient(circle, rgba(64, 72, 220, 0.4), transparent 65%); + animation: drift3 22s ease-in-out infinite alternate; +} +@keyframes drift1 { to { transform: translate(9vw, 7vh) scale(1.15); } } +@keyframes drift2 { to { transform: translate(-8vw, 10vh) scale(0.9); } } +@keyframes drift3 { to { transform: translate(6vw, -8vh) scale(1.1); } } + +/* 科技网格:向下的渐隐遮罩 */ +.hero-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(76, 195, 255, 0.06) 1px, transparent 1px), + linear-gradient(90deg, rgba(76, 195, 255, 0.06) 1px, transparent 1px); + background-size: 56px 56px; + mask-image: linear-gradient(180deg, transparent 0%, #000 40%, transparent 92%); + -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 40%, transparent 92%); +} + +.hero-inner { position: relative; z-index: 1; padding-top: 68px; } +.hero-eyebrow { + font-size: 14px; + letter-spacing: 0.2em; + color: var(--cyan); + font-weight: 600; + margin-bottom: 22px; + display: flex; + align-items: center; + gap: 12px; +} +/* eyebrow 前的脉冲光点:生命感 */ +.hero-eyebrow::before { + content: ""; + width: 9px; height: 9px; + border-radius: 50%; + background: var(--cyan); + box-shadow: 0 0 12px 2px rgba(76, 195, 255, 0.8); + animation: pulse 2.2s ease-in-out infinite; +} +@keyframes pulse { 50% { opacity: 0.35; } } + +.hero-title { + font-size: clamp(42px, 7vw, 84px); + font-weight: 800; + line-height: 1.15; + letter-spacing: 0.01em; + margin-bottom: 26px; + text-wrap: balance; +} +.hero-sub { max-width: 640px; color: var(--muted); font-size: 17px; margin-bottom: 40px; } +.hero-actions { display: flex; gap: 16px; flex-wrap: wrap; } + +.btn { + display: inline-block; + padding: 13px 34px; + border-radius: 999px; + font-size: 15.5px; + font-weight: 600; + transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; +} +.btn-primary { + background: var(--grad); + color: #fff; + box-shadow: 0 8px 30px rgba(76, 195, 255, 0.28); +} +.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 12px 44px rgba(255, 61, 90, 0.4); } +.btn-ghost { border: 1px solid var(--line); color: var(--text); } +.btn-ghost:hover { border-color: var(--cyan); transform: translateY(-2px); } + +/* ============================================================ + 技术词条跑马灯 + ============================================================ */ +.marquee { + border-block: 1px solid var(--line); + background: rgba(148, 178, 255, 0.03); + overflow: hidden; + position: relative; +} +/* 两侧渐隐,词条像从光雾里滚出 */ +.marquee::before, .marquee::after { + content: ""; + position: absolute; + top: 0; bottom: 0; + width: 120px; + z-index: 1; + pointer-events: none; +} +.marquee::before { left: 0; background: linear-gradient(90deg, var(--bg-0), transparent); } +.marquee::after { right: 0; background: linear-gradient(-90deg, var(--bg-0), transparent); } +.marquee-track { + display: flex; + gap: 48px; + width: max-content; + padding: 16px 0; + animation: scroll 36s linear infinite; +} +.marquee-track span { + font-size: 14px; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--muted); + white-space: nowrap; + display: flex; + align-items: center; + gap: 48px; +} +.marquee-track span::after { content: "✦"; color: var(--brand); font-size: 11px; } +@keyframes scroll { to { transform: translateX(-50%); } } + +/* ============================================================ + 区块通用 + ============================================================ */ +.section { padding: 104px 0; } +.section-alt { + background: var(--bg-1); + /* 顶部一条极细渐变分隔线,代替粗边框 */ + border-block: 1px solid transparent; + border-image: linear-gradient(90deg, transparent, rgba(76, 195, 255, 0.35), transparent) 1; +} + +.section-head { margin-bottom: 52px; max-width: 780px; } +.eyebrow { + font-size: 13px; + font-weight: 700; + letter-spacing: 0.28em; + color: var(--brand); + margin-bottom: 12px; + display: flex; + align-items: center; + gap: 10px; +} +.eyebrow::before { content: ""; width: 22px; height: 1px; background: var(--brand); } +.section-head h2 { font-size: clamp(30px, 4vw, 44px); font-weight: 800; line-height: 1.25; } +.section-desc { color: var(--muted); margin-top: 16px; font-size: 16.5px; } + +/* ============================================================ + 关于 — Bento 网格 + ============================================================ */ +.bento { display: grid; grid-template-columns: repeat(4, 1fr); grid-auto-rows: auto; gap: 18px; } +.bento-text { grid-column: 1 / 3; padding: 30px 32px; } +.bento-photo { grid-column: 3 / 5; min-height: 300px; } +.bento-stat { padding: 22px; } +.bento-text, .bento-photo, .bento-stat { + border-radius: var(--radius); + border: 1px solid var(--line); + background: var(--bg-2); + position: relative; + overflow: hidden; +} +/* Bento 卡顶部一线品牌光 */ +.bento-text::before, .bento-photo::before, .bento-stat::before { + content: ""; + position: absolute; + inset: 0 0 auto 0; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(76, 195, 255, 0.5), transparent); +} +.bento-text p { color: var(--muted); margin-bottom: 16px; font-size: 15px; } +.bento-text p:first-child { color: var(--text); } +/* 完整显示不裁剪:PPT 原图左右各约 40% 会被 cover 裁掉(CCTV 标志即因此丢失) */ +.bento-photo { display: flex; } +.bento-photo img { + width: 100%; + height: auto; + max-height: 100%; + object-fit: contain; + margin: auto; + border-radius: 8px; +} +.bento-stat strong { display: block; font-size: 19px; margin-bottom: 5px; } +.bento-stat span { font-size: 13px; color: var(--muted); } +.bento-stat:hover { border-color: rgba(76, 195, 255, 0.45); transition: border-color 0.25s; } + +/* ============================================================ + 痛点 + ============================================================ */ +.pain-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; margin-bottom: 52px; } +.pain-grid-3 { grid-template-columns: repeat(3, 1fr); } +.pain { + border: 1px solid var(--line); + border-radius: 12px; + padding: 16px 18px; + background: var(--bg-2); +} +/* 左上角缺角高亮条:批次感 */ +.pain::before { + content: ""; + display: block; + width: 26px; height: 3px; + border-radius: 2px; + background: var(--grad); + margin-bottom: 10px; +} +.pain h4 { font-size: 15px; margin-bottom: 4px; } +.pain p { font-size: 13px; color: var(--muted); } + +/* ============================================================ + 产品卡 — 发光描边 + 镂空图 + ============================================================ */ +.product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 22px; } +.product-grid-3 { grid-template-columns: repeat(3, 1fr); } + +.card { + position: relative; + border-radius: var(--radius); + overflow: hidden; + background: + linear-gradient(var(--bg-1), var(--bg-1)) padding-box, + linear-gradient(140deg, rgba(76, 195, 255, 0.4), rgba(148, 178, 255, 0.08) 40%, rgba(255, 61, 90, 0.35)) border-box; + border: 1px solid transparent; + transition: transform 0.25s, box-shadow 0.25s, background 0.25s; +} +.card:hover { + transform: translateY(-6px); + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5), 0 0 30px rgba(76, 195, 255, 0.12); + background: + linear-gradient(var(--bg-1), var(--bg-1)) padding-box, + linear-gradient(140deg, rgba(76, 195, 255, 0.75), rgba(148, 178, 255, 0.15) 40%, rgba(255, 61, 90, 0.65)) border-box; +} + +/* 镂空产品图的舞台:底部品牌辉光托底 */ +.product-media { + display: flex; + align-items: center; + justify-content: center; + padding: 34px 28px; + min-height: 250px; + background: + radial-gradient(90% 70% at 50% 105%, rgba(76, 195, 255, 0.16), transparent 60%), + radial-gradient(50% 40% at 50% 100%, rgba(255, 61, 90, 0.10), transparent 70%), + var(--bg-0); +} +.product-media img { + max-height: 230px; + width: auto; + max-width: 100%; + filter: drop-shadow(0 18px 30px rgba(0, 0, 0, 0.55)); + transition: transform 0.3s; +} +.card:hover .product-media img { transform: scale(1.06); } +/* 背景复杂的实拍图:不镂空,统一描边容器 */ +.product-media img.framed { + border-radius: 12px; + border: 1px solid rgba(233, 238, 248, 0.22); + box-shadow: 0 14px 36px rgba(0, 0, 0, 0.45); + filter: none; +} + +.product-body { padding: 20px 22px 24px; } +.product-body h3 { font-size: 17px; margin-bottom: 12px; } +.product-note { font-size: 13.5px; color: var(--muted); margin-bottom: 12px; } + +/* 功能标签:等宽网格对齐,杜绝换行参差 */ +.tags { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); + gap: 7px; +} +.tags span { + font-size: 12px; + color: var(--cyan); + background: rgba(76, 195, 255, 0.08); + border: 1px solid rgba(76, 195, 255, 0.22); + padding: 5px 4px; + border-radius: 8px; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +/* 英文模式:标签词更长,放宽格子并缩小字号防截断 */ +html[lang="en"] .tags { grid-template-columns: repeat(auto-fill, minmax(124px, 1fr)); } +html[lang="en"] .tags span { font-size: 11px; } + +.feature-row { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: stretch; } +.product-hero { display: flex; flex-direction: column; } +.product-hero .product-media { flex: 1; min-height: 340px; } +.product-hero .product-media img { max-height: 300px; } +.scene-side { border-radius: var(--radius); border: 1px solid var(--line); object-fit: cover; width: 100%; } + +/* ============================================================ + 场景图 + ============================================================ */ +.scene-grid { display: grid; grid-template-columns: 2fr 1fr; gap: 22px; margin-top: 22px; } +.scene-grid img { + border-radius: var(--radius); + border: 1px solid var(--line); + height: 100%; + object-fit: cover; + transition: transform 0.3s; +} +.scene-grid img:hover { transform: scale(1.015); } + +/* ============================================================ + 平台/定制卡 + ============================================================ */ +.platform-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; } +.platform-media { + position: relative; + background: var(--bg-0); + display: flex; + align-items: flex-end; + justify-content: center; + padding: 22px; + min-height: 230px; + overflow: hidden; +} +.platform-media::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient(120% 90% at 50% 0%, rgba(76, 195, 255, 0.10), transparent 60%); +} + +/* 多图卡:局部滑动展示。截图统一高度、宽度按原始比例自适应, + 超宽截图自然超出卡片形成"滑动看局部",宽度永远匹配内容 */ +.slider { + display: block; + overflow-x: auto; + scroll-snap-type: x mandatory; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} +.slider::-webkit-scrollbar { display: none; } +.slides { display: flex; gap: 16px; align-items: flex-end; } +.slides figure { + flex: 0 0 auto; + scroll-snap-align: center; + margin: 0 4px; + display: flex; + justify-content: center; +} +.slides img { + height: 176px; + width: auto; + max-width: none; + border-radius: 10px; + border: 1px solid var(--line); + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5); +} +/* 手机竖屏截图稍高,底对齐时视觉平衡 */ +.slides img.shot-phone { height: 196px; } +/* 指示点 */ +.dots { + position: absolute; + left: 0; right: 0; bottom: 8px; + display: flex; + justify-content: center; + gap: 7px; + z-index: 2; +} +.dots button { + width: 7px; height: 7px; + border-radius: 50%; + border: none; + padding: 0; + cursor: pointer; + background: rgba(148, 178, 255, 0.25); + transition: background 0.2s, transform 0.2s; +} +.dots button.active { + background: var(--cyan); + transform: scale(1.35); + box-shadow: 0 0 8px rgba(76, 195, 255, 0.7); +} +.shot-main { + width: 100%; + max-width: 420px; + border-radius: 10px; + border: 1px solid var(--line); + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5); +} +.shot-wide { max-width: 480px; } +.shot-phone { + width: 96px; + flex-shrink: 0; + border-radius: 12px; + border: 1px solid var(--line); + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.5); +} +.feature-list li { + position: relative; + padding-left: 22px; + font-size: 14px; + color: var(--muted); + margin-bottom: 9px; +} +.feature-list li::before { + content: ""; + position: absolute; + left: 0; + top: 9px; + width: 7px; + height: 7px; + border-radius: 2px; + background: var(--grad); +} + +/* ============================================================ + 联系 + ============================================================ */ +.contact { + background: + radial-gradient(70% 100% at 50% 100%, rgba(255, 61, 90, 0.10), transparent 70%), + var(--bg-1); + text-align: center; +} +.contact-inner { max-width: 720px; } +.contact-title { + font-size: clamp(34px, 5vw, 56px); + font-weight: 800; + display: flex; + gap: 22px; + justify-content: center; + flex-wrap: wrap; +} +.contact-sub { color: var(--muted); margin: 18px 0 44px; } +.contact-card { + display: inline-flex; + align-items: center; + gap: 36px; + padding: 32px 44px; + border-radius: 20px; + background: + linear-gradient(var(--bg-1), var(--bg-1)) padding-box, + linear-gradient(140deg, rgba(76, 195, 255, 0.4), rgba(255, 61, 90, 0.4)) border-box; + border: 1px solid transparent; + text-align: left; +} +.qrcode { width: 148px; height: 148px; border-radius: 12px; } +.contact-mail { + font-size: 21px; + font-weight: 700; + background: var(--grad); + -webkit-background-clip: text; + background-clip: text; + color: transparent; + display: inline-block; + margin-bottom: 10px; +} +.contact-mail:hover { filter: brightness(1.2); } +.contact-info p { font-size: 14px; color: var(--muted); margin-bottom: 4px; } + +/* ============================================================ + Footer + ============================================================ */ +.site-footer { border-top: 1px solid var(--line); padding: 30px 0; } +.footer-wrap { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; } +.footer-brand { display: flex; align-items: center; gap: 14px; } +.footer-brand .brand-logo { height: 30px; } +.footer-brand span { font-size: 14px; color: var(--muted); } +.footer-copy { font-size: 13px; color: var(--muted); } + +/* ============================================================ + 滚动淡入 + ============================================================ */ +.reveal { opacity: 0; transform: translateY(26px); transition: opacity 0.7s ease, transform 0.7s ease; } +.reveal.visible { opacity: 1; transform: none; } + +/* ============================================================ + 响应式 + ============================================================ */ +@media (max-width: 1024px) { + .product-grid, .pain-grid, .platform-grid { grid-template-columns: repeat(2, 1fr); } + .pain-grid-3 { grid-template-columns: repeat(2, 1fr); } + .bento { grid-template-columns: repeat(2, 1fr); } + .bento-text, .bento-photo { grid-column: 1 / 3; } + .bento-photo { min-height: 240px; } +} + +@media (max-width: 768px) { + .section { padding: 72px 0; } + + .menu-toggle { display: flex; margin-left: auto; } + .lang-toggle { margin-left: auto; } + .menu-toggle { margin-left: 0; } + .nav { + position: fixed; + top: 68px; + left: 0; right: 0; + flex-direction: column; + align-items: flex-start; + padding: 20px 24px 28px; + gap: 18px; + background: rgba(4, 7, 15, 0.96); + backdrop-filter: blur(16px); + border-bottom: 1px solid var(--line); + display: none; + } + .nav.open { display: flex; } + + .hero { min-height: 88vh; } + .hero-sub { font-size: 15.5px; } + .aurora { filter: blur(60px); } + + .feature-row, .scene-grid { grid-template-columns: 1fr; } + .stats { gap: 14px; } + .contact-card { flex-direction: column; text-align: center; gap: 20px; padding: 28px; } +} + +@media (max-width: 520px) { + .product-grid, .pain-grid, .pain-grid-3, .platform-grid, .bento { grid-template-columns: 1fr; } + .bento-text, .bento-photo { grid-column: auto; } + .hero-actions .btn { width: 100%; text-align: center; } + .product-media { min-height: 210px; } +} + +/* 尊重系统减弱动效偏好 */ +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + .aurora i, .marquee-track, .hero-eyebrow::before { animation: none; } + .reveal { opacity: 1; transform: none; transition: none; } + .card, .btn, .bento-stat { transition: none; } +} diff --git a/assets/favicon.svg b/assets/favicon.svg new file mode 100644 index 0000000..a777907 --- /dev/null +++ b/assets/favicon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/assets/img/about-base.webp b/assets/img/about-base.webp new file mode 100644 index 0000000..63d571f Binary files /dev/null and b/assets/img/about-base.webp differ diff --git a/assets/img/about-main.webp b/assets/img/about-main.webp new file mode 100644 index 0000000..b7b7fdd Binary files /dev/null and b/assets/img/about-main.webp differ diff --git a/assets/img/about-secondary.webp b/assets/img/about-secondary.webp new file mode 100644 index 0000000..63d571f Binary files /dev/null and b/assets/img/about-secondary.webp differ diff --git a/assets/img/ai-agent.jpg b/assets/img/ai-agent.jpg new file mode 100644 index 0000000..ce7ec41 Binary files /dev/null and b/assets/img/ai-agent.jpg differ diff --git a/assets/img/ai-agent.webp b/assets/img/ai-agent.webp new file mode 100644 index 0000000..a8e5035 Binary files /dev/null and b/assets/img/ai-agent.webp differ diff --git a/assets/img/app-livestock.jpg b/assets/img/app-livestock.jpg new file mode 100644 index 0000000..9c1b834 Binary files /dev/null and b/assets/img/app-livestock.jpg differ diff --git a/assets/img/app-livestock.webp b/assets/img/app-livestock.webp new file mode 100644 index 0000000..9eb5dd3 Binary files /dev/null and b/assets/img/app-livestock.webp differ diff --git a/assets/img/asset-scene-1.webp b/assets/img/asset-scene-1.webp new file mode 100644 index 0000000..ec0fcb5 Binary files /dev/null and b/assets/img/asset-scene-1.webp differ diff --git a/assets/img/asset-scene-2.webp b/assets/img/asset-scene-2.webp new file mode 100644 index 0000000..40b21fb Binary files /dev/null and b/assets/img/asset-scene-2.webp differ diff --git a/assets/img/base-station-bg01.webp b/assets/img/base-station-bg01.webp new file mode 100644 index 0000000..5f10020 Binary files /dev/null and b/assets/img/base-station-bg01.webp differ diff --git a/assets/img/bt-tag-st02.webp b/assets/img/bt-tag-st02.webp new file mode 100644 index 0000000..03727f2 Binary files /dev/null and b/assets/img/bt-tag-st02.webp differ diff --git a/assets/img/building.jpg b/assets/img/building.jpg new file mode 100644 index 0000000..5f1226d Binary files /dev/null and b/assets/img/building.jpg differ diff --git a/assets/img/building.webp b/assets/img/building.webp new file mode 100644 index 0000000..f2270f4 Binary files /dev/null and b/assets/img/building.webp differ diff --git a/assets/img/cctv.jpg b/assets/img/cctv.jpg new file mode 100644 index 0000000..b8199ad Binary files /dev/null and b/assets/img/cctv.jpg differ diff --git a/assets/img/cctv.webp b/assets/img/cctv.webp new file mode 100644 index 0000000..876c113 Binary files /dev/null and b/assets/img/cctv.webp differ diff --git a/assets/img/collar-sg03-mini.webp b/assets/img/collar-sg03-mini.webp new file mode 100644 index 0000000..a8a74a1 Binary files /dev/null and b/assets/img/collar-sg03-mini.webp differ diff --git a/assets/img/collar-sg03.webp b/assets/img/collar-sg03.webp new file mode 100644 index 0000000..3e1c65e Binary files /dev/null and b/assets/img/collar-sg03.webp differ diff --git a/assets/img/custom-agent.webp b/assets/img/custom-agent.webp new file mode 100644 index 0000000..8a5662e Binary files /dev/null and b/assets/img/custom-agent.webp differ diff --git a/assets/img/custom-liangchang-1.webp b/assets/img/custom-liangchang-1.webp new file mode 100644 index 0000000..22c1037 Binary files /dev/null and b/assets/img/custom-liangchang-1.webp differ diff --git a/assets/img/custom-liangchang-2.webp b/assets/img/custom-liangchang-2.webp new file mode 100644 index 0000000..5a959ae Binary files /dev/null and b/assets/img/custom-liangchang-2.webp differ diff --git a/assets/img/custom-simulation.webp b/assets/img/custom-simulation.webp new file mode 100644 index 0000000..402d656 Binary files /dev/null and b/assets/img/custom-simulation.webp differ diff --git a/assets/img/digital-twin.jpg b/assets/img/digital-twin.jpg new file mode 100644 index 0000000..0ae6cc1 Binary files /dev/null and b/assets/img/digital-twin.jpg differ diff --git a/assets/img/digital-twin.webp b/assets/img/digital-twin.webp new file mode 100644 index 0000000..9b8dcdb Binary files /dev/null and b/assets/img/digital-twin.webp differ diff --git a/assets/img/ear-tag-st01.webp b/assets/img/ear-tag-st01.webp new file mode 100644 index 0000000..d91b27a Binary files /dev/null and b/assets/img/ear-tag-st01.webp differ diff --git a/assets/img/factory-line.jpg b/assets/img/factory-line.jpg new file mode 100644 index 0000000..bc04565 Binary files /dev/null and b/assets/img/factory-line.jpg differ diff --git a/assets/img/factory-line.webp b/assets/img/factory-line.webp new file mode 100644 index 0000000..087716f Binary files /dev/null and b/assets/img/factory-line.webp differ diff --git a/assets/img/factory.jpg b/assets/img/factory.jpg new file mode 100644 index 0000000..9362aba Binary files /dev/null and b/assets/img/factory.jpg differ diff --git a/assets/img/factory.webp b/assets/img/factory.webp new file mode 100644 index 0000000..723c6d8 Binary files /dev/null and b/assets/img/factory.webp differ diff --git a/assets/img/favicon.png b/assets/img/favicon.png new file mode 100644 index 0000000..e45ac58 Binary files /dev/null and b/assets/img/favicon.png differ diff --git a/assets/img/goat.jpg b/assets/img/goat.jpg new file mode 100644 index 0000000..e3a680e Binary files /dev/null and b/assets/img/goat.jpg differ diff --git a/assets/img/goat.webp b/assets/img/goat.webp new file mode 100644 index 0000000..74ebfb3 Binary files /dev/null and b/assets/img/goat.webp differ diff --git a/assets/img/grazing-scene-1.webp b/assets/img/grazing-scene-1.webp new file mode 100644 index 0000000..68590df Binary files /dev/null and b/assets/img/grazing-scene-1.webp differ diff --git a/assets/img/grazing-scene-2.webp b/assets/img/grazing-scene-2.webp new file mode 100644 index 0000000..ae88f5c Binary files /dev/null and b/assets/img/grazing-scene-2.webp differ diff --git a/assets/img/hero-bg.webp b/assets/img/hero-bg.webp new file mode 100644 index 0000000..10dbe26 Binary files /dev/null and b/assets/img/hero-bg.webp differ diff --git a/assets/img/hero-grazing.jpg b/assets/img/hero-grazing.jpg new file mode 100644 index 0000000..39b3afe Binary files /dev/null and b/assets/img/hero-grazing.jpg differ diff --git a/assets/img/hero-grazing.webp b/assets/img/hero-grazing.webp new file mode 100644 index 0000000..ab7a69f Binary files /dev/null and b/assets/img/hero-grazing.webp differ diff --git a/assets/img/hydro-class.jpg b/assets/img/hydro-class.jpg new file mode 100644 index 0000000..05bef2b Binary files /dev/null and b/assets/img/hydro-class.jpg differ diff --git a/assets/img/hydro-class.webp b/assets/img/hydro-class.webp new file mode 100644 index 0000000..b48b471 Binary files /dev/null and b/assets/img/hydro-class.webp differ diff --git a/assets/img/hydroponics-hs01.webp b/assets/img/hydroponics-hs01.webp new file mode 100644 index 0000000..36f0597 Binary files /dev/null and b/assets/img/hydroponics-hs01.webp differ diff --git a/assets/img/liangchang.jpg b/assets/img/liangchang.jpg new file mode 100644 index 0000000..15a9a67 Binary files /dev/null and b/assets/img/liangchang.jpg differ diff --git a/assets/img/liangchang.webp b/assets/img/liangchang.webp new file mode 100644 index 0000000..3258bde Binary files /dev/null and b/assets/img/liangchang.webp differ diff --git a/assets/img/logo-light.png b/assets/img/logo-light.png new file mode 100644 index 0000000..2723498 Binary files /dev/null and b/assets/img/logo-light.png differ diff --git a/assets/img/logo-light.webp b/assets/img/logo-light.webp new file mode 100644 index 0000000..927fbe0 Binary files /dev/null and b/assets/img/logo-light.webp differ diff --git a/assets/img/logo.png b/assets/img/logo.png new file mode 100644 index 0000000..bebf103 Binary files /dev/null and b/assets/img/logo.png differ diff --git a/assets/img/machine-install.jpg b/assets/img/machine-install.jpg new file mode 100644 index 0000000..6422060 Binary files /dev/null and b/assets/img/machine-install.jpg differ diff --git a/assets/img/machine-install.webp b/assets/img/machine-install.webp new file mode 100644 index 0000000..66aa77c Binary files /dev/null and b/assets/img/machine-install.webp differ diff --git a/assets/img/p-bb03.png b/assets/img/p-bb03.png new file mode 100644 index 0000000..830dca1 Binary files /dev/null and b/assets/img/p-bb03.png differ diff --git a/assets/img/p-bb03.webp b/assets/img/p-bb03.webp new file mode 100644 index 0000000..7958748 Binary files /dev/null and b/assets/img/p-bb03.webp differ diff --git a/assets/img/p-bg01.png b/assets/img/p-bg01.png new file mode 100644 index 0000000..e3a79d0 Binary files /dev/null and b/assets/img/p-bg01.png differ diff --git a/assets/img/p-bg01.webp b/assets/img/p-bg01.webp new file mode 100644 index 0000000..cbf5244 Binary files /dev/null and b/assets/img/p-bg01.webp differ diff --git a/assets/img/p-hs01.png b/assets/img/p-hs01.png new file mode 100644 index 0000000..7d79d21 Binary files /dev/null and b/assets/img/p-hs01.png differ diff --git a/assets/img/p-hs01.webp b/assets/img/p-hs01.webp new file mode 100644 index 0000000..dca92fa Binary files /dev/null and b/assets/img/p-hs01.webp differ diff --git a/assets/img/p-sg03.png b/assets/img/p-sg03.png new file mode 100644 index 0000000..991a1a0 Binary files /dev/null and b/assets/img/p-sg03.png differ diff --git a/assets/img/p-sg03.webp b/assets/img/p-sg03.webp new file mode 100644 index 0000000..110e3ff Binary files /dev/null and b/assets/img/p-sg03.webp differ diff --git a/assets/img/p-sg03mini.png b/assets/img/p-sg03mini.png new file mode 100644 index 0000000..478b474 Binary files /dev/null and b/assets/img/p-sg03mini.png differ diff --git a/assets/img/p-sg03mini.webp b/assets/img/p-sg03mini.webp new file mode 100644 index 0000000..5e76898 Binary files /dev/null and b/assets/img/p-sg03mini.webp differ diff --git a/assets/img/p-sl01.png b/assets/img/p-sl01.png new file mode 100644 index 0000000..e22ab46 Binary files /dev/null and b/assets/img/p-sl01.png differ diff --git a/assets/img/p-sl01.webp b/assets/img/p-sl01.webp new file mode 100644 index 0000000..34e5ee1 Binary files /dev/null and b/assets/img/p-sl01.webp differ diff --git a/assets/img/p-st01.png b/assets/img/p-st01.png new file mode 100644 index 0000000..cb4130b Binary files /dev/null and b/assets/img/p-st01.png differ diff --git a/assets/img/p-st01.webp b/assets/img/p-st01.webp new file mode 100644 index 0000000..43c0f37 Binary files /dev/null and b/assets/img/p-st01.webp differ diff --git a/assets/img/p-st02.png b/assets/img/p-st02.png new file mode 100644 index 0000000..4022645 Binary files /dev/null and b/assets/img/p-st02.png differ diff --git a/assets/img/p-st02.webp b/assets/img/p-st02.webp new file mode 100644 index 0000000..0e69798 Binary files /dev/null and b/assets/img/p-st02.webp differ diff --git a/assets/img/planting-scene.webp b/assets/img/planting-scene.webp new file mode 100644 index 0000000..538add3 Binary files /dev/null and b/assets/img/planting-scene.webp differ diff --git a/assets/img/platform-asset-1.webp b/assets/img/platform-asset-1.webp new file mode 100644 index 0000000..68ab938 Binary files /dev/null and b/assets/img/platform-asset-1.webp differ diff --git a/assets/img/platform-asset-2.webp b/assets/img/platform-asset-2.webp new file mode 100644 index 0000000..12b8557 Binary files /dev/null and b/assets/img/platform-asset-2.webp differ diff --git a/assets/img/platform-asset.jpg b/assets/img/platform-asset.jpg new file mode 100644 index 0000000..32fdf3e Binary files /dev/null and b/assets/img/platform-asset.jpg differ diff --git a/assets/img/platform-asset.webp b/assets/img/platform-asset.webp new file mode 100644 index 0000000..14f19ce Binary files /dev/null and b/assets/img/platform-asset.webp differ diff --git a/assets/img/platform-grazing-1.webp b/assets/img/platform-grazing-1.webp new file mode 100644 index 0000000..10f46ef Binary files /dev/null and b/assets/img/platform-grazing-1.webp differ diff --git a/assets/img/platform-grazing-app.webp b/assets/img/platform-grazing-app.webp new file mode 100644 index 0000000..f32ed20 Binary files /dev/null and b/assets/img/platform-grazing-app.webp differ diff --git a/assets/img/platform-hydro.jpg b/assets/img/platform-hydro.jpg new file mode 100644 index 0000000..10420f2 Binary files /dev/null and b/assets/img/platform-hydro.jpg differ diff --git a/assets/img/platform-hydro.webp b/assets/img/platform-hydro.webp new file mode 100644 index 0000000..6b5f2d9 Binary files /dev/null and b/assets/img/platform-hydro.webp differ diff --git a/assets/img/platform-pasture.jpg b/assets/img/platform-pasture.jpg new file mode 100644 index 0000000..6b24739 Binary files /dev/null and b/assets/img/platform-pasture.jpg differ diff --git a/assets/img/platform-pasture.webp b/assets/img/platform-pasture.webp new file mode 100644 index 0000000..3db0776 Binary files /dev/null and b/assets/img/platform-pasture.webp differ diff --git a/assets/img/platform-planting-1.webp b/assets/img/platform-planting-1.webp new file mode 100644 index 0000000..09452f8 Binary files /dev/null and b/assets/img/platform-planting-1.webp differ diff --git a/assets/img/platform-planting-2.webp b/assets/img/platform-planting-2.webp new file mode 100644 index 0000000..d08ae68 Binary files /dev/null and b/assets/img/platform-planting-2.webp differ diff --git a/assets/img/qrcode.png b/assets/img/qrcode.png new file mode 100644 index 0000000..86202e1 Binary files /dev/null and b/assets/img/qrcode.png differ diff --git a/assets/img/qrcode.webp b/assets/img/qrcode.webp new file mode 100644 index 0000000..a44fbf6 Binary files /dev/null and b/assets/img/qrcode.webp differ diff --git a/assets/img/terminal-bb03.webp b/assets/img/terminal-bb03.webp new file mode 100644 index 0000000..4276a5b Binary files /dev/null and b/assets/img/terminal-bb03.webp differ diff --git a/assets/img/uwb-tag-sl01.webp b/assets/img/uwb-tag-sl01.webp new file mode 100644 index 0000000..8f9ebeb Binary files /dev/null and b/assets/img/uwb-tag-sl01.webp differ diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..3bc59c3 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,227 @@ +/* ============================================================ + 沙香科技官网交互:中英切换 / 滚动淡入 / 导航 + 中文为 HTML 默认内容(SEO 友好),英文通过本字典替换 + ============================================================ */ +(function () { + 'use strict'; + + /* ---------- 英文字典(key 对应 data-i18n) ---------- */ + var EN = { + // 导航 + 'nav.about': 'About', 'nav.grazing': 'Digital Grazing', 'nav.planting': 'Smart Planting', + 'nav.asset': 'Asset Management', 'nav.platform': 'Software Platform', + 'nav.custom': 'Custom Development', 'nav.contact': 'Contact Us', + // Hero + 'hero.eyebrow': 'Suzhou Shashann Technology · AIoT', + 'hero.title1': 'Powering Digital', 'hero.title2': 'Transformation with AIoT', + 'hero.sub': 'Artificial intelligence, edge computing, cloud computing and low-power IoT — smart hardware products and digital software platforms that measurably boost efficiency and productivity across industries.', + 'hero.cta1': 'Explore Solutions', 'hero.cta2': 'Contact Us', + // 关于 + 'about.title': 'About Shashann', + 'about.p1': 'Suzhou Shashann Technology is an AIoT company dedicated to digital upgrading across industries — combining AI, edge computing, cloud computing and low-power IoT to deliver smart hardware products and digital software service platforms that significantly improve efficiency and productivity while cutting production and management costs.', + 'about.p2': 'At the 10th anniversary of the Belt and Road Initiative, our self-developed smart livestock product was named a "China Signature Product" and was featured in episode 6 of the documentary "The Light of Technology". Our portfolio — smart collars, smart ear tags, smart hydroponic grow racks, smart terminals and smart asset tags — is already deployed across smart grazing, smart planting and smart asset management.', + 'about.p3': 'Our core team brings deep AIoT expertise and partners with leading universities for continuous innovation, with R&D and production bases in Nanjing, Zhangjiagang and Changzhou, and products sold overseas. Guided by "Customer First, Innovation Driven", we aim to be a trusted, industry-leading AIoT company.', + 'about.stat1v': 'China Signature Product', 'about.stat1k': 'Belt & Road 10th Anniversary', + 'about.stat2v': 'The Light of Technology', 'about.stat2k': 'Featured in documentary Ep.6', + 'about.stat3v': '3 Bases', 'about.stat3k': 'Nanjing · Zhangjiagang · Changzhou', + 'about.stat4v': 'Sold Overseas', 'about.stat4k': 'Deployed at scale in many sectors', + // 数字畜牧 + 'grazing.title': 'Digital Grazing', + 'grazing.desc': 'Livestock farming is geographically scattered with long grazing cycles, long dependent on manual inspection and experience. Our collars, ear tags and Bluetooth base stations turn grazing from "by experience" into "by data".', + 'grazing.pain1t': 'Low Production Efficiency', 'grazing.pain1d': 'Each herder can only look after a limited number of livestock.', + 'grazing.pain2t': 'No Early Warning', 'grazing.pain2d': 'Wide distribution and mobility make status monitoring and traceability difficult.', + 'grazing.pain3t': 'Hard Insurance Claims', 'grazing.pain3d': 'Key events like epidemics, deaths and losses lack objective data.', + 'grazing.pain4t': 'Ecology Unmeasured', 'grazing.pain4d': 'Grassland capacity and ecological change are hard to quantify and close the loop.', + 'grazing.p1n': 'Smart Collar SG03 mini', + 'grazing.p2n': 'Smart Collar SG03', + 'grazing.p3n': 'Smart Bluetooth Ear Tag ST01', + 'grazing.p4n': 'Smart Bluetooth Base Station BG01', + // 智慧种植 + 'planting.title': 'Smart Planting', + 'planting.desc': 'Urban residents increasingly want to grow food at home, and school labor courses and AI education need better carriers. Smart hydroponics is convenient, intelligent and clean — we turn it from an expensive toy into a system that actually grows.', + 'planting.pain1t': 'For Users', 'planting.pain1d': 'Beginners fail often; one bad experience kills repurchase.', + 'planting.pain2t': 'For Products', 'planting.pain2d': 'Fake intelligence and immature consumables leave no returning revenue.', + 'planting.pain3t': 'For the Industry', 'planting.pain3d': 'The category is still seen as an expensive toy with no leading brand.', + 'planting.p1n': 'Smart Hydroponic Grow Rack HS01', + 'planting.p1note': 'Standalone cabinet design, easy to expand; supports unattended AI mode and manual control.', + // 设备资产 + 'asset.title': 'Digital Equipment & Asset Management', + 'asset.desc': 'Enterprises hold large, widely distributed asset pools that manual ledgers and periodic patrols cannot cover. IoT and AI make full lifecycle management possible — from "reactive repair" to "predictive maintenance".', + 'asset.pain1t': 'Invisible Field', 'asset.pain1d': 'Ledgers never match reality; counting assets still means walking around.', + 'asset.pain2t': 'No Traceability', 'asset.pain2d': 'Lack of recorded data makes quality issues hard to locate.', + 'asset.pain3t': 'Fragmented Systems', 'asset.pain3d': 'Multiple systems, ununified data, weak decision support.', + 'asset.pain4t': 'Reactive Maintenance', 'asset.pain4d': 'Repair-after-failure dominates; downtime costs far exceed maintenance.', + 'asset.p1n': 'Smart Terminal BB03', + 'asset.p1note': 'High-accuracy edge computing that reliably recognizes device/asset state.', + 'asset.p2n': 'UWB Smart Tag SL01', + 'asset.p2note': 'Works with safety helmets; easy to install.', + 'asset.p3n': 'Bluetooth Smart Tag ST02', + // 软件平台 + 'platform.title': 'Software Platform', + 'platform.desc': 'Every hardware line ships with a companion SaaS platform, APP and WeChat mini program — device data flows straight to the cloud, management is not tied to any terminal.', + 'platform.s1n': 'Smart Grazing System', + 'platform.s1a': 'SaaS platform, APP and WeChat mini program', + 'platform.s1b': 'Multi-account ranch management, precision tracking and geofence alarms', + 'platform.s1c': 'Livestock vitals monitoring, voice calling and music playback', + 'platform.s1d': 'Device management with full lifecycle data and traceability', + 'platform.s2n': 'Smart Hydroponics System', + 'platform.s2a': 'SaaS platform and APP', + 'platform.s2b': 'Multi-account planting management with live status', + 'platform.s2c': 'Automatic planting adjustment system', + 'platform.s2d': 'AI planting assistant', + 'platform.s3n': 'Smart Asset Management System', + 'platform.s3a': 'SaaS platform and WeChat mini program', + 'platform.s3b': 'Multi-account asset and maintenance management', + 'platform.s3c': 'Real-time computation of asset state and location', + 'platform.s3d': 'Accurate prediction and maintenance from historical data', + // 定制开发 + 'custom.title': 'Custom Software Development', + 'custom.desc': 'Building on our engineering and simulation expertise, we deliver custom systems for industry clients — from digital twins to multi-agent workflows.', + 'custom.s1n': 'Smart Beam Yard System', + 'custom.s1a': 'SaaS platform and WeChat mini program', + 'custom.s1b': 'Resettlement progress, bridge and production management', + 'custom.s1c': 'Material management, multi-sensor data access', + 'custom.s1d': 'Digital twin system', + 'custom.s2n': 'Intelligent Simulation Platform', + 'custom.s2a': 'Browser/server computing platform', + 'custom.s2b': 'Structural mechanics simulation', + 'custom.s2c': 'Thermal and fluid simulation', + 'custom.s2d': 'AI-accelerated simulation', + 'custom.s3n': 'Custom AI Agent Systems', + 'custom.s3a': 'Multi-agent workflows', + 'custom.s3b': 'AI agent optimization', + 'custom.s3c': 'Features tailored to customer needs', + // 联系 + 'contact.motto1': 'Customer First,', 'contact.motto2': 'Innovation Driven', + 'contact.sub': 'Scan the QR code, or email us about your digital upgrade needs.', + 'contact.company': 'Suzhou Shashann Technology Co., Ltd.', + 'contact.loc': 'R&D & Production Bases: Nanjing · Zhangjiagang · Changzhou', + // Footer + 'footer.slogan': 'Powering digital transformation with AIoT', + 'footer.name': 'Suzhou Shashann Technology Co., Ltd.', + 'footer.motto': 'Customer First, Innovation Driven', + // 功能标签(跨产品复用) + 'tag.geofence': 'Geofence Alarm', 'tag.find': 'Smart Find', 'tag.vitals': 'Vitals Monitoring', + 'tag.wireless': 'Long Battery / Wireless Charging', 'tag.solar': 'Solar Powered', 'tag.voice': 'Voice Calling', + 'tag.satellite': 'Satellite Comms', 'tag.herd': 'Smart Herding', 'tag.mesh': 'Mesh Networking', + 'tag.lost': 'Loss Alarm', 'tag.led': 'LED Find', 'tag.connect1000': '1000+ Connections', + 'tag.hiloc': 'Precision Tracking', 'tag.music': 'Music Playback', 'tag.bind': 'Tag Binding', + 'tag.cluster': 'Cluster Alarm', + 'tag.sensorx': 'Multi-sensor', 'tag.light': 'Smart Lighting', 'tag.water': 'Auto Water Circulation', + 'tag.fert': 'Auto Fertilizing', 'tag.aimode': 'Unattended AI Mode', 'tag.lowpower': 'Ultra-low Power', + 'tag.stick': 'Stick & Play', 'tag.rugged': 'Rugged', 'tag.cm': 'Centimeter Positioning', + 'tag.fall': 'Fall Detection', 'tag.easy': 'Easy Install', 'tag.buzz': 'Buzzer Alarm' + }; + + var lang = localStorage.getItem('lang') || 'zh'; + + /* ---------- 语言切换 ---------- */ + var nodes = Array.prototype.slice.call(document.querySelectorAll('[data-i18n]')); + // 首次加载时记录中文原文,供切回使用 + nodes.forEach(function (el) { el.setAttribute('data-zh', el.textContent); }); + + function applyLang(l) { + lang = l; + localStorage.setItem('lang', l); + document.documentElement.lang = l === 'zh' ? 'zh-CN' : 'en'; + nodes.forEach(function (el) { + var key = el.getAttribute('data-i18n'); + var text = l === 'en' ? EN[key] : el.getAttribute('data-zh'); + if (text != null) el.textContent = text; + }); + document.getElementById('langToggle').textContent = l === 'zh' ? 'EN' : '中文'; + document.title = l === 'zh' + ? '苏州沙香科技有限公司 — AIoT 智能硬件与数字化平台' + : 'Suzhou Shashann Technology — AIoT Smart Hardware & Digital Platforms'; + } + + document.getElementById('langToggle').addEventListener('click', function () { + applyLang(lang === 'zh' ? 'en' : 'zh'); + }); + + applyLang(lang); + + /* ---------- 版权当前年份自动跨年(起始年 2023 写死在 HTML) ---------- */ + document.getElementById('copyrightYear').textContent = String(new Date().getFullYear()); + + /* ---------- 导航滚动态 ---------- */ + var header = document.getElementById('siteHeader'); + function onScroll() { header.classList.toggle('scrolled', window.scrollY > 10); } + window.addEventListener('scroll', onScroll, { passive: true }); + onScroll(); + + /* ---------- 移动端菜单 ---------- */ + var menuToggle = document.getElementById('menuToggle'); + var nav = document.getElementById('siteNav'); + menuToggle.addEventListener('click', function () { + var open = nav.classList.toggle('open'); + menuToggle.classList.toggle('open', open); + }); + // 点击菜单项后自动收起 + nav.addEventListener('click', function (e) { + if (e.target.tagName === 'A') { + nav.classList.remove('open'); + menuToggle.classList.remove('open'); + } + }); + + /* ---------- 多图卡滑动指示点 ---------- */ + document.querySelectorAll('.slider').forEach(function (slider) { + var slides = slider.querySelector('.slides'); + var figures = Array.prototype.slice.call(slides.querySelectorAll('figure')); + if (figures.length < 2) return; // 单图卡不需要指示点 + + var dots = document.createElement('div'); + dots.className = 'dots'; + var buttons = figures.map(function (_, i) { + var b = document.createElement('button'); + b.type = 'button'; + b.setAttribute('aria-label', 'Slide ' + (i + 1)); + b.addEventListener('click', function () { + // 滚到第 i 张居中(配合 scroll-snap-type: x mandatory) + var f = figures[i]; + slides.scrollTo({ + left: f.offsetLeft - (slides.offsetWidth - f.offsetWidth) / 2, + behavior: 'smooth' + }); + }); + dots.appendChild(b); + return b; + }); + slider.appendChild(dots); + + // 初始把第一张居中(无动画;scroll-snap 保证落点合法) + slides.scrollLeft = + figures[0].offsetLeft - (slides.offsetWidth - figures[0].offsetWidth) / 2; + + // 滚动时高亮离视口中心最近的图 + function update() { + var center = slides.scrollLeft + slides.offsetWidth / 2; + var best = 0, bestDist = Infinity; + figures.forEach(function (f, i) { + var d = Math.abs(f.offsetLeft + f.offsetWidth / 2 - center); + if (d < bestDist) { bestDist = d; best = i; } + }); + buttons.forEach(function (b, i) { b.classList.toggle('active', i === best); }); + } + slides.addEventListener('scroll', function () { + window.requestAnimationFrame(update); + }, { passive: true }); + update(); + }); + + /* ---------- 滚动淡入 ---------- */ + if ('IntersectionObserver' in window) { + var io = new IntersectionObserver(function (entries) { + entries.forEach(function (entry) { + if (entry.isIntersecting) { + entry.target.classList.add('visible'); + io.unobserve(entry.target); + } + }); + }, { threshold: 0.12 }); + document.querySelectorAll('.reveal').forEach(function (el) { io.observe(el); }); + } else { + document.querySelectorAll('.reveal').forEach(function (el) { el.classList.add('visible'); }); + } +})(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..2ce5338 --- /dev/null +++ b/index.html @@ -0,0 +1,368 @@ + + + + + + 苏州沙香科技有限公司 — AIoT 智能硬件与数字化平台 + + + + + + + + + + + + + +
+ +
+ + +
+

苏州沙香科技有限公司 · AIoT

+

用 AIoT 驱动
产业数字化升级

+

人工智能 · 边缘计算 · 云计算 · 低功耗物联网——为各行业提供智能硬件产品与数字化应用软件服务平台,显著提升效率与生产力。

+ +
+
+ + + + + +
+
+
+

ABOUT SHASHANN

+

关于沙香

+
+
+
+

苏州沙香科技有限公司是一家 AIoT 科技公司,致力于用人工智能、边缘计算、云计算、低功耗物联网等技术,为各行业的数字化升级提供智能硬件产品和数字化改造等的应用软件服务平台,显著提升其效率及生产力,并能减少各类生产和管理成本。

+

在"一带一路"战略项目十周年庆祝活动中,自研的智能畜牧产品被评为"中国名片产品",并参与了大型纪录片《科技之光》第六集的拍摄。自研的其他智能硬件包括智能项圈、智能耳标、智能水培种植架、智能终端、智能资产标签等产品,已经广泛应用在智能放牧、智能种植、智能设备资产管理等多个领域。

+

公司核心团队有着丰富的 AIoT 相关技术经验,并与国内一流大学合作,不断进行技术创新。在南京、张家港、常州等地设有研发生产基地,部分产品远销海外。公司秉持"客户第一,创新驱动"的发展理念,努力成为行业领先、值得伙伴信赖的 AIoT 科技企业。

+
+
+ 研发生产基地 +
+
中国名片产品"一带一路"十周年评选
+
《科技之光》大型纪录片第六集拍摄
+
3 地基地南京 · 张家港 · 常州
+
远销海外多领域规模化应用
+
+
+
+ + +
+
+
+

DIGITAL GRAZING

+

数字畜牧

+

畜牧业地域分散、放养周期长,长期依赖人工巡查与经验管理。我们用智能项圈、耳标与蓝牙基站构建数字化放牧体系,让牲畜管理从"靠经验"走向"靠数据"。

+
+
+

生产经营效率低

每个牧民照看管理的牲畜数量有限。

+

疫情等风险难预警

牲畜分布广、流动性强,状态难监测,产业链溯源难。

+

保险难理赔

防疫、死亡、丢失等关键环节缺乏客观数据支撑。

+

生态难以量化

草原承载与生态变化难以评估,规模化经营难以闭环。

+
+
+
+
智能项圈 SG03 mini
+
+

智能项圈 SG03 mini

+
+ 围栏报警智能寻找生理监测长续航/无线充电 +
+
+
+
+
智能项圈 SG03
+
+

智能项圈 SG03

+
+ 太阳能供电语音召唤智能寻找生理监测卫星通信智能驱使音乐播放 +
+
+
+
+
智能蓝牙耳标 ST01
+
+

智能蓝牙耳标 ST01

+
+ 集群组网丢失报警亮灯寻找生理监测 +
+
+
+
+
智能蓝牙基站 BG01
+
+

智能蓝牙基站 BG01

+
+ 超 1000 连接数高精定位集群报警唯一绑定耳标 +
+
+
+
+
+
+ + +
+
+
+

SMART PLANTING

+

智慧种植

+

城市居民"在家种菜"的需求正在兴起,中小学劳动课与 AI 思维训练也需要更好的载体。智能水培方便、智能、干净——我们让它从"贵玩具"变成真正种得活、留得住的智能种植系统。

+
+
+

用户层面

种植小白失败率高,一次坏体验直接劝退复购。

+

产品层面

智能化是伪智能,耗材体系不成熟,厂商赚不到回头钱。

+

行业层面

品类认知停留在"贵玩具",没有品牌完成心智收割。

+
+
+
+
智能水培种植架 HS01
+
+

智能水培种植架 HS01

+

独立柜设计,方便扩展;支持无人 AI 种植模式与手动控制模式。

+
+ 多复合传感器智能防日光补光自动水循环自动施肥无人 AI 种植 +
+
+
+ 智慧种植典型场景 +
+
+
+ + +
+
+
+

DIGITAL ASSET MANAGEMENT

+

数字设备与资产管理

+

企业资产体量大、分布广,传统人工台账与定期巡检难以为继。IoT 与 AI 让设备全生命周期管理成为可能,助力从"被动维修"升级为"预测性运维"。

+
+
+

现场不可视

资产家底不清,台账与实物长期对不上,盘点靠人跑。

+

过程不可追溯

缺乏数据留痕,质量问题难以定位。

+

系统割裂严重

多系统并存,数据不统一,难以支撑决策。

+

设备运维被动

事后维修为主,停机损失远大于维护成本。

+
+
+
+
智能终端 BB03
+
+

智能终端 BB03

+

边缘计算算法精度高,准确识别设备/资产当前状态。

+
+ 多复合传感器高精定位超低功耗即粘即用结实耐用 +
+
+
+
+
UWB 智能标签 SL01
+
+

UWB 智能标签 SL01

+

可配合安全帽使用,安装方便。

+
+ 厘米级定位跌落报警超低功耗安装方便 +
+
+
+
+
蓝牙智能标签 ST02
+
+

蓝牙智能标签 ST02

+
+ 亮灯寻找蜂鸣报警超低功耗安装方便 +
+
+
+
+
+
+ + +
+
+
+

SOFTWARE PLATFORM

+

产品配套软件平台

+

每一类智能硬件,都有配套的 SaaS 平台、APP 与微信小程序,硬件数据直达云端,管理不受终端限制。

+
+
+
+
+
+
智能放牧系统
+
智能放牧系统 APP
+
+
+
+

智能放牧系统

+
    +
  • SaaS 平台、APP 与微信小程序
  • +
  • 多账户牧场管理,高精定位与围栏报警
  • +
  • 牲畜生理状态监测,语音召唤与音乐播放
  • +
  • 智能硬件管理,全生命周期数据管理与溯源
  • +
+
+
+
+
+
+
智能水培种植系统
+
智能水培种植系统
+
+
+
+

智能水培种植系统

+
    +
  • SaaS 平台与 APP
  • +
  • 多账户种植管理,种植状态实时展示
  • +
  • 自动种植调节系统
  • +
  • AI 智能助手
  • +
+
+
+
+
+
+
智能设备资产管理系统
+
智能设备资产管理系统
+
+
+
+

智能设备资产管理系统

+
    +
  • SaaS 平台与微信小程序
  • +
  • 多账户资产管理与维护管理
  • +
  • 资产状态、位置等信息实时计算
  • +
  • 根据历史数据精准预测与维护
  • +
+
+
+
+
+
+ + +
+
+
+

CUSTOM DEVELOPMENT

+

定制软件系统开发

+

基于核心团队的工程与仿真能力,面向行业客户提供定制化系统开发:从数字孪生到智能体工作流。

+
+
+
+
+
+
智慧梁场系统
+
智慧梁场系统
+
+
+
+

智慧梁场系统

+
    +
  • SaaS 平台与微信小程序
  • +
  • 征迁进度、桥梁管理与生产管理
  • +
  • 物料管理,多传感器数据接入
  • +
  • 数字孪生系统
  • +
+
+
+
+
+ 智能仿真计算平台 +
+
+

智能仿真计算平台

+
    +
  • BS 架构计算平台
  • +
  • 力学仿真计算系统
  • +
  • 热与流体仿真计算系统
  • +
  • AI 加速仿真计算系统
  • +
+
+
+
+
+ 智能体定制开发系统 +
+
+

智能体定制开发系统

+
    +
  • 多智能体工作流
  • +
  • AI 智能体优化
  • +
  • 按客户需求定制功能
  • +
+
+
+
+
+
+ + +
+
+

CONTACT US

+

客户第一创新驱动

+

扫描二维码,或发邮件与我们聊聊您的数字化需求。

+
+ 联系二维码 +
+ contact@shashann.com +

苏州沙香科技有限公司

+

研发生产基地:南京 · 张家港 · 常州

+
+
+
+
+
+ + + + + + diff --git a/tools/deploy.sh b/tools/deploy.sh new file mode 100644 index 0000000..173fb7a --- /dev/null +++ b/tools/deploy.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# 官网统一部署脚本:bump 静态资源版本号(击穿浏览器缓存)+ 同步双服务器 +# 用法: bash tools/deploy.sh +set -euo pipefail +cd "$(dirname "$0")/.." + +STAMP=$(date "+%Y%m%d%H%M") + +# bump 资源版本参数:HTML 更新(no-cache)会带新 URL,旧缓存自然失效 +python3 - "$STAMP" <<'PYEOF' +import re, sys +stamp = sys.argv[1] +path = 'index.html' +html = open(path, encoding='utf-8').read() +# 正则一次性规范化:无论已有无版本参数,统一替换为当前时间戳 +html = re.sub(r'assets/css/style\.css(?:\?v=\d+)?\d*', 'assets/css/style.css?v=' + stamp, html) +html = re.sub(r'assets/js/main\.js(?:\?v=\d+)?\d*', 'assets/js/main.js?v=' + stamp, html) +open(path, 'w', encoding='utf-8').write(html) +PYEOF + +echo "[deploy] 版本号 ${STAMP},同步公网 + 局域网" +# 注意:assets 不带尾斜杠(带斜杠会把目录拍平,曾导致整站 404) +rsync -a --delete index.html assets ubuntu3:/var/www/shashann-homepage/ +rsync -a --delete index.html assets ubuntu:/home/ubuntu/homepage-site/ +echo "[deploy] 完成: https://www.shashann.com/ 与 https://192.168.51.66/homepage/" diff --git a/tools/gen_contact_avatar.py b/tools/gen_contact_avatar.py new file mode 100644 index 0000000..7b2da43 --- /dev/null +++ b/tools/gen_contact_avatar.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +""" +生成 contact 公共邮箱头像:品牌深底 + 青→红渐变 @ 符号 +视觉规范与官网一致(assets/css/style.css 的 --bg/--cyan/--brand) +用法: uv run --with pillow python3 tools/gen_contact_avatar.py +""" +from PIL import Image, ImageDraw, ImageFilter, ImageFont + +SIZE = 512 +BG = (7, 11, 22) # 官网 --bg-1 +CYAN = (76, 195, 255) # 官网 --cyan +RED = (255, 61, 90) # 官网 --brand +OUT = 'assets/brand/contact-avatar.png' + +# ---------- 背景:深底 + 左上青辉光 / 右下红辉光 ---------- +bg = Image.new('RGB', (SIZE, SIZE), BG) + +def glow_blob(box, blur): + blob = Image.new('L', (SIZE, SIZE), 0) + ImageDraw.Draw(blob).ellipse(box, fill=80) + return blob.filter(ImageFilter.GaussianBlur(blur)) + +bg.paste(Image.new('RGB', (SIZE, SIZE), CYAN), (0, 0), glow_blob((-220, -220, 240, 240), 100)) +bg.paste(Image.new('RGB', (SIZE, SIZE), RED), (0, 0), glow_blob((SIZE - 240, SIZE - 240, SIZE + 220, SIZE + 220), 100)) + +# ---------- 对角渐变图(青 → 红),用作 @ 的填充色 ---------- +c1, c2 = CYAN, RED +span = 2 * (SIZE - 1) +grad = Image.new('RGB', (SIZE, SIZE)) +grad.putdata([ + ( + round(c1[0] + (c2[0] - c1[0]) * (x + y) / span), + round(c1[1] + (c2[1] - c1[1]) * (x + y) / span), + round(c1[2] + (c2[2] - c1[2]) * (x + y) / span), + ) + for y in range(SIZE) for x in range(SIZE) +]) + +# ---------- 渲染 @ 文字 mask ---------- +# macOS 西文字体按优先级找,@ 字形各字体差异不大 +FONT_CANDIDATES = [ + '/System/Library/Fonts/SFNS.ttf', + '/System/Library/Fonts/SFNSText.ttf', + '/System/Library/Fonts/Helvetica.ttc', +] +font_path = next((p for p in FONT_CANDIDATES if __import__('os').path.exists(p)), None) +if not font_path: + raise SystemExit('找不到可用系统字体: ' + ', '.join(FONT_CANDIDATES)) + +font = ImageFont.truetype(font_path, 400) +mask = Image.new('L', (SIZE, SIZE), 0) +ImageDraw.Draw(mask).text((SIZE // 2, SIZE // 2), '@', font=font, fill=255, anchor='mm') + +# 自检:@ 必须真的渲染出来了,否则静默输出空底图 +ink = sum(1 for v in mask.getdata() if v > 0) +ratio = ink / (SIZE * SIZE) +if not 0.05 < ratio < 0.45: + raise SystemExit(f'@ 渲染异常,墨迹占比 {ratio:.1%}(预期 5%~45%),检查字体 {font_path}') + +bg.paste(grad, (0, 0), mask) + +import os +os.makedirs(os.path.dirname(OUT), exist_ok=True) +bg.save(OUT, 'PNG', optimize=True) +print(f'完成: {OUT} {SIZE}x{SIZE} @{ratio:.0%} 字体={font_path}') diff --git a/tools/make_favicon.py b/tools/make_favicon.py new file mode 100644 index 0000000..edb3d2b --- /dev/null +++ b/tools/make_favicon.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +从反白 logo 提取左侧图形生成 favicon(180x180 PNG): +深色圆角底(与站内卡片底色一致)+ 图形居中占 72% +标签栏明暗模式均以深色块呈现,反白图形恒清晰。 +用法: uv run --with pillow python3 tools/make_favicon.py +""" +from PIL import Image, ImageDraw + +SRC = 'assets/img/logo-light.png' +OUT = 'assets/img/favicon.png' +SIZE = 180 +PAD_GRAPHIC = 0.72 # 图形占画布比例 + +# logo 左侧图形实测内容段(x 投影分析):x[47:224] y[44:260],取 bbox 略收边 +CROP = (47, 44, 224, 260) +BG = (7, 11, 22) # 站内 --bg-1 +RADIUS = 38 + +graphic = Image.open(SRC).convert('RGBA').crop(CROP) + +canvas = Image.new('RGBA', (SIZE, SIZE), (0, 0, 0, 0)) +# 圆角深色底 +mask = Image.new('L', (SIZE, SIZE), 0) +ImageDraw.Draw(mask).rounded_rectangle((0, 0, SIZE, SIZE), RADIUS, fill=255) +canvas.paste(Image.new('RGBA', (SIZE, SIZE), BG + (255,)), (0, 0), mask) + +# 图形等比缩放到 72% 高度内居中 +target = round(SIZE * PAD_GRAPHIC) +scale = min(target / graphic.width, target / graphic.height) +graphic = graphic.resize((round(graphic.width * scale), round(graphic.height * scale)), Image.LANCZOS) +canvas.alpha_composite(graphic, ((SIZE - graphic.width) // 2, (SIZE - graphic.height) // 2)) + +# 自检:中心应有不透明图形像素,四角透明 +px = canvas.load() +assert px[SIZE // 2, SIZE // 2][3] > 0, '中心无图形' +assert px[2, 2][3] == 0 and px[SIZE - 3, SIZE - 3][3] == 0, '圆角未生效' +canvas.save(OUT, 'PNG', optimize=True) +print(f'完成: {OUT} {SIZE}x{SIZE},图形 {graphic.size}') diff --git a/tools/make_logo_light.py b/tools/make_logo_light.py new file mode 100644 index 0000000..b311d78 --- /dev/null +++ b/tools/make_logo_light.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +生成深色站反白版 logo(logo-light.png): +1. flood-fill 镂空深蓝底(容差 84 = v1 验证成功参数,能吃掉 #000020 底色) +2. 不透明内容亮度反转 L' = 1 - L(黑字→白字,红/青/绿提亮,色相保留) +深色页黑字看不清、白底块又突兀,反白是深色站标准解法。 +用法: uv run --with pillow python3 tools/make_logo_light.py +""" +import colorsys +from collections import deque + +from PIL import Image + +SRC = 'assets/img/logo.png' +OUT = 'assets/img/logo-light.png' +TOL = 84 # 曼哈顿距离容差(v1 实测:清除 83%、红色主体完整保留) + +im = Image.open(SRC).convert('RGBA') +w, h = im.size +px = im.load() + +# --- flood-fill 清底 --- +corners = [px[0, 0], px[w-1, 0], px[0, h-1], px[w-1, h-1]] +bg = tuple(sum(c[i] for c in corners) // 4 for i in range(3)) + +def is_bg(p): + if p[3] < 10: + return True + return abs(p[0]-bg[0]) + abs(p[1]-bg[1]) + abs(p[2]-bg[2]) <= TOL + +seen = [[False]*w for _ in range(h)] +q = deque() +for x in range(w): + q.extend(((x, 0), (x, h-1))) +for y in range(h): + q.extend(((0, y), (w-1, y))) +cleared = 0 +while q: + x, y = q.popleft() + if x < 0 or x >= w or y < 0 or y >= h or seen[y][x]: + continue + seen[y][x] = True + p = px[x, y] + if not is_bg(p): + continue + px[x, y] = (0, 0, 0, 0) + cleared += 1 + q.extend(((x+1, y), (x-1, y), (x, y+1), (x, y-1))) +ratio = cleared / (w * h) +assert 0.10 < ratio < 0.92, f'镂空异常: 清除 {ratio:.0%}' + +# --- 暗部提亮、亮部保留(logo 含 ~20% 浅色元素,全量反转会把它们变黑) --- +# L' = max(L, 1-L):暗像素翻到亮侧(黑→白),亮像素不动,色相全保留 +opaque = [px[x, y] for y in range(h) for x in range(w) if px[x, y][3] > 128] +assert opaque, '镂空后内容为空' + +for y in range(h): + for x in range(w): + r, g, b, a = px[x, y] + if a == 0: + continue + hh, ll, ss = colorsys.rgb_to_hls(r / 255, g / 255, b / 255) + r2, g2, b2 = colorsys.hls_to_rgb(hh, max(ll, 1 - ll), ss) + px[x, y] = (round(r2 * 255), round(g2 * 255), round(b2 * 255), a) + +im.save(OUT, 'PNG', optimize=True) + +# 验证:反白后所有不透明像素亮度下限 +final = [px[x, y] for y in range(h) for x in range(w) if px[x, y][3] > 128] +dark = sum(1 for p in final if 0.299*p[0]+0.587*p[1]+0.114*p[2] < 90) / len(final) +print(f'完成: {OUT} 镂空 {ratio:.0%},暗部像素剩 {dark:.1%}(应为 0)') diff --git a/tools/process_images.py b/tools/process_images.py new file mode 100644 index 0000000..deea234 --- /dev/null +++ b/tools/process_images.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +""" +从 PPT 提取的原始素材生成网站用图: +1. logo 与 7 张黑底产品图:边缘 flood-fill 镂空(背景变透明,适配深色网页) +2. 水培架图背景复杂(39% 均匀度),不镂空,走统一描边容器展示 +3. 截图/场景/照片转 WebP 压缩;二维码保持 PNG 无损 +用法: uv run --with pillow python3 tools/process_images.py /ppt/media <输出目录> +""" +import sys +from collections import deque +from pathlib import Path + +from PIL import Image + +src = Path(sys.argv[1]) +out = Path(sys.argv[2]) +out.mkdir(parents=True, exist_ok=True) + +RENAME = { + 'image2.png': 'logo.png', # 原样无损输出(见 PASS_THROUGH) + 'image1.jpeg': 'hero-bg.webp', # v2 起不再做 hero 背景,仅作 og:image + 'image3.png': 'about-main.webp', # v2 起弃用(PPT 拼贴图有白缝) + 'image4.png': 'about-base.webp', + 'image5.png': 'collar-sg03-mini.webp', + 'image13.png': 'collar-sg03.webp', + 'image10.png': 'ear-tag-st01.webp', + 'image11.png': 'base-station-bg01.webp', + 'image12.png': 'grazing-scene-1.webp', + 'image18.png': 'grazing-scene-2.webp', + 'image19.png': 'hydroponics-hs01.webp', + 'image20.png': 'planting-scene.webp', + 'image31.png': 'terminal-bb03.webp', + 'image32.png': 'uwb-tag-sl01.webp', + 'image33.png': 'bt-tag-st02.webp', + 'image29.png': 'asset-scene-1.webp', + 'image30.jpeg': 'asset-scene-2.webp', + 'image34.png': 'platform-grazing-1.webp', + 'image35.png': 'platform-grazing-app.webp', + 'image36.png': 'platform-planting-1.webp', + 'image37.png': 'platform-planting-2.webp', + 'image38.png': 'platform-asset-1.webp', + 'image39.png': 'platform-asset-2.webp', + 'image40.png': 'custom-liangchang-1.webp', + 'image41.png': 'custom-liangchang-2.webp', + 'image42.png': 'custom-simulation.webp', + 'image43.png': 'custom-agent.webp', + 'image44.png': 'qrcode.png', +} + +# 这些图做边缘 flood-fill 镂空(检测过:边缘色 96-100% 均匀的纯黑底) +# 注意:logo 不镂空——其底色 #000020 与四角检测色差超容差,v2 曾清成斑驳残留导致看不清 +CUTOUT = { + 'image5.png', 'image13.png', 'image10.png', 'image11.png', + 'image31.png', 'image32.png', 'image33.png', +} +CUTOUT_TOL = 30 # 曼哈顿距离阈值,宁小勿大:黑底纯度高,产品暗部 (#20 系) 距离约 96 不会被误清 + +def cutout_bg(path: Path, tol: int) -> Image.Image: + """从画布四边缘 flood-fill 清除均匀底色,只清与边缘连通的背景,不动被包围的内容""" + im = Image.open(path).convert('RGBA') + w, h = im.size + px = im.load() + corners = [px[0, 0], px[w-1, 0], px[0, h-1], px[w-1, h-1]] + bg = tuple(sum(c[i] for c in corners) // 4 for i in range(3)) + + def is_bg(p): + if p[3] < 10: + return True # 原本就是透明的像素直接视为背景 + return abs(p[0]-bg[0]) + abs(p[1]-bg[1]) + abs(p[2]-bg[2]) <= tol + + seen = [[False]*w for _ in range(h)] + q = deque() + for x in range(w): + q.extend(((x, 0), (x, h-1))) + for y in range(h): + q.extend(((0, y), (w-1, y))) + cleared = 0 + while q: + x, y = q.popleft() + if x < 0 or x >= w or y < 0 or y >= h or seen[y][x]: + continue + seen[y][x] = True + p = px[x, y] + if not is_bg(p): + continue + px[x, y] = (0, 0, 0, 0) + cleared += 1 + q.extend(((x+1, y), (x-1, y), (x, y+1), (x, y-1))) + ratio = cleared / (w * h) + # 镂空比例健全性检查:太低=没清干净,太高=误伤内容 + if not 0.10 < ratio < 0.92: + raise SystemExit(f'镂空异常 {path.name}: 清除 {ratio:.0%},需人工检查') + return im + +# 原样无损拷贝:logo 色块文字经不起有损压缩,二维码必须无损 +PASS_THROUGH = {'image2.png', 'image44.png'} + +for src_name, dst_name in RENAME.items(): + src_path = src / src_name + if not src_path.exists(): + print(f' 跳过缺失: {src_name}') + continue + dst_path = out / dst_name + if src_name in PASS_THROUGH: + Image.open(src_path).save(dst_path) + elif src_name in CUTOUT: + im = cutout_bg(src_path, CUTOUT_TOL) + # 镂空图必须带 alpha,webp 无损存 alpha(有损会弄脏半透明边缘) + im.save(dst_path, 'WEBP', quality=100, lossless=True, method=6) + else: + im = Image.open(src_path).convert('RGB') + if im.width > 1600: + im = im.resize((1600, round(im.height * 1600 / im.width)), Image.LANCZOS) + im.save(dst_path, 'WEBP', quality=82, method=6) + kb = dst_path.stat().st_size // 1024 + tag = ' [镂空]' if src_name in CUTOUT else '' + print(f' {src_name:14s} -> {dst_name:28s} {kb}KB{tag}') + +print(f'完成,共 {len(RENAME)} 张 -> {out}') diff --git a/tools/process_new_assets.py b/tools/process_new_assets.py new file mode 100644 index 0000000..12e0c38 --- /dev/null +++ b/tools/process_new_assets.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +""" +处理用户补充的新素材(assets/img 内): +- 7 张透明底产品图 p-*:无损 WebP 保留 alpha(已是镂空,无需再处理) +- p-hs01 背景复杂不透明:有损压缩(页面走 framed 描边容器) +- 12 张照片 JPG:有损 WebP(宽度 >1600 的缩到 1600) +- qr-wechat.png 二维码:不处理,HTML 直接引用原 PNG +原图不删除(用户提供的源文件)。用法: uv run --with pillow python3 tools/process_new_assets.py +""" +from pathlib import Path + +from PIL import Image + +IMG = Path('assets/img') + +# 已检测为真透明的产品图(透明像素 37%~88%) +ALPHA_PRODUCTS = [ + 'p-bb03.png', 'p-bg01.png', 'p-sg03.png', 'p-sg03mini.png', + 'p-sl01.png', 'p-st01.png', 'p-st02.png', +] +# 有损压缩的照片/截图 +PHOTOS = [ + 'p-hs01.png', # 背景复杂不透明,同照片处理 + 'ai-agent.jpg', 'app-livestock.jpg', 'building.jpg', 'cctv.jpg', + 'digital-twin.jpg', 'factory-line.jpg', 'factory.jpg', 'goat.jpg', + 'hero-grazing.jpg', 'hydro-class.jpg', 'liangchang.jpg', + 'machine-install.jpg', 'platform-asset.jpg', 'platform-hydro.jpg', + 'platform-pasture.jpg', +] + +for name in ALPHA_PRODUCTS: + src = IMG / name + dst = IMG / (name[:-4] + '.webp') + im = Image.open(src).convert('RGBA') + # 自检:透明像素占比必须显著,否则说明误判为透明图 + trans = sum(1 for p in im.getdata() if p[3] < 250) / (im.width * im.height) + assert trans > 0.2, f'{name} 透明像素仅 {trans:.0%},不符合透明底预期' + im.save(dst, 'WEBP', lossless=True, method=6) + print(f'{name:18s} -> {dst.name:22s} 透明{trans:.0%} {dst.stat().st_size // 1024}KB') + +for name in PHOTOS: + src = IMG / name + dst = IMG / (name[:-4] + '.webp') + im = Image.open(src).convert('RGB') + if im.width > 1600: + im = im.resize((1600, round(im.height * 1600 / im.width)), Image.LANCZOS) + im.save(dst, 'WEBP', quality=82, method=6) + print(f'{name:18s} -> {dst.name:22s} {im.width}x{im.height} {dst.stat().st_size // 1024}KB') + +print('完成')