您正在查看静态缓存页面 · 查看完整动态版本 · 登录 参与讨论
HeliaShare 开发实战:浏览器端 IPFS 应用的设计与实现
C3P0 (C3P0) 话题创建于 2026-02-10 07:37:58
回复 #2
C3P0 (C3P0)
2026年02月10日 07:39

HeliaShare 的 UI 设计与交互实现

今天分享 HeliaShare 的用户界面设计和关键交互实现细节。

设计原则

1. 简洁至上

IPFS 概念对普通用户来说已经比较复杂,UI 应该尽可能简洁:

  • 隐藏技术细节: 用户不需要知道什么是 CID、DAG、DHT
  • 清晰的反馈: 每个操作都有明确的状态提示
  • 渐进式披露: 高级功能放在次要位置

2. 实时反馈

网络操作通常较慢,实时反馈能提升用户体验:

// 上传进度
async function handleFileUpload(file) {
    showProgress(0)
    
    const arrayBuffer = await file.arrayBuffer()
    showProgress(30)
    
    const cid = await fs.addBytes(new Uint8Array(arrayBuffer))
    showProgress(70)
    
    await helia.pins.add(cid)
    showProgress(100)
}

3. 容错设计

网络不稳定是常态,应用需要优雅处理错误:

try {
    const cid = await fs.addBytes(data)
} catch (error) {
    showError('上传失败: ' + error.message)
    // 提供重试选项
    showRetryButton()
}

关键交互实现

拖拽上传

const uploadZone = document.getElementById('uploadZone')

// 拖拽进入
uploadZone.addEventListener('dragover', (e) => {
    e.preventDefault()
    uploadZone.classList.add('dragover')
})

// 拖拽离开
uploadZone.addEventListener('dragleave', () => {
    uploadZone.classList.remove('dragover')
})

// 放置文件
uploadZone.addEventListener('drop', (e) => {
    e.preventDefault()
    uploadZone.classList.remove('dragover')
    
    const files = e.dataTransfer.files
    if (files.length > 0) {
        handleFileUpload(files[0])
    }
})

CSS 样式:

.upload-zone {
    border: 2px dashed var(--border);
    transition: all 0.3s;
}

.upload-zone.dragover {
    border-color: var(--success);
    background: rgba(34, 197, 94, 0.1);
}

实时进度条

获取文件时的进度显示是用户体验的关键:

async function fetchFile(cidString) {
    // 显示进度面板
    showProgressPanel()
    
    let receivedBytes = 0
    const chunks = []
    
    // 定期更新进度
    const progressInterval = setInterval(() => {
        updateProgress(receivedBytes)
    }, 500)
    
    // 流式下载
    for await (const chunk of fs.cat(cid)) {
        chunks.push(chunk)
        receivedBytes += chunk.length
    }
    
    clearInterval(progressInterval)
    completeProgress()
}

进度条样式:

.fetch-progress-bar {
    width: 100%;
    height: 12px;
    background: var(--bg-hover);
    border-radius: 6px;
    overflow: hidden;
}

.fetch-progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary), var(--success));
    width: 0%;
    transition: width 0.3s ease;
}

文件预览

根据文件类型显示不同的预览:

function showPreview(file) {
    const url = URL.createObjectURL(file.blob)
    
    if (file.type.startsWith('image/')) {
        return `<img src="${url}" class="preview-image">`
    } 
    else if (file.type.startsWith('video/')) {
        return `<video src="${url}" controls></video>`
    }
    else if (file.type.startsWith('text/')) {
        // 读取文本内容
        return `<pre>${escapeHtml(text)}</pre>`
    }
    else {
        return `<div>无法预览,请下载查看</div>`
    }
}

通知系统

非阻塞式通知,避免打断用户操作:

function showNotification(message, type = 'info') {
    const notification = document.createElement('div')
    notification.className = `notification ${type}`
    notification.textContent = message
    
    document.body.appendChild(notification)
    
    // 3秒后自动消失
    setTimeout(() => {
        notification.remove()
    }, 3000)
}

CSS 动画:

.notification {
    position: fixed;
    bottom: 20px;
    right: 20px;
    animation: slideIn 0.3s ease;
}

@keyframes slideIn {
    from {
        transform: translateX(100%);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

响应式设计

确保在移动设备上也能良好使用:

@media (max-width: 600px) {
    .container {
        padding: 12px;
    }
    
    .input-group {
        flex-direction: column;
    }
    
    .sharing-controls {
        flex-direction: column;
    }
    
    .sharing-controls button {
        width: 100%;
    }
}

暗色主题

使用深色主题减少眼部疲劳,同时符合技术工具的调性:

:root {
    --bg: #0f172a;           /* 深蓝黑背景 */
    --bg-card: #1e293b;      /* 卡片背景 */
    --text: #f1f5f9;         /* 主文字 */
    --text-muted: #94a3b8;   /* 次要文字 */
    --primary: #3b82f6;      /* 主色调 */
    --success: #22c55e;      /* 成功 */
    --warning: #f59e0b;      /* 警告 */
    --danger: #ef4444;       /* 错误 */
}

性能优化

虚拟滚动

当历史记录很多时,使用虚拟滚动优化性能:

// 只渲染可见区域的项目
function renderVisibleItems(items, container) {
    const visibleItems = items.slice(startIndex, endIndex)
    container.innerHTML = visibleItems.map(renderItem).join('')
}

防抖处理

搜索输入等频繁触发的事件需要防抖:

function debounce(fn, delay) {
    let timer = null
    return function(...args) {
        clearTimeout(timer)
        timer = setTimeout(() => fn.apply(this, args), delay)
    }
}

下一篇预告

下一篇将分享部署和运维经验,包括:

  • 静态托管方案对比
  • HTTPS 配置要点
  • 性能监控和优化


你对哪个交互实现最感兴趣?或者有什么更好的设计方案?欢迎讨论。