|
@@ -1,505 +1,75 @@
|
|
-<!DOCTYPE html>
|
|
|
|
-<html lang="zh-CN">
|
|
|
|
-<head>
|
|
|
|
- <meta charset="UTF-8">
|
|
|
|
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
- <title>简易链接管理器</title>
|
|
|
|
- <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.2/papaparse.min.js"></script>
|
|
|
|
- <style>
|
|
|
|
- body {
|
|
|
|
- font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
|
|
- line-height: 1.6;
|
|
|
|
- margin: 0;
|
|
|
|
- padding: 20px;
|
|
|
|
- color: #333;
|
|
|
|
- background-color: #f9f9f9;
|
|
|
|
- }
|
|
|
|
- .container {
|
|
|
|
- max-width: 1200px;
|
|
|
|
- margin: 0 auto;
|
|
|
|
- }
|
|
|
|
- .header {
|
|
|
|
- display: flex;
|
|
|
|
- justify-content: space-between;
|
|
|
|
- align-items: center;
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
- }
|
|
|
|
- h1 {
|
|
|
|
- color: #2c3e50;
|
|
|
|
- margin: 0;
|
|
|
|
- }
|
|
|
|
- .toolbar {
|
|
|
|
- display: flex;
|
|
|
|
- gap: 10px;
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
- }
|
|
|
|
- .tag-list {
|
|
|
|
- display: flex;
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
- gap: 8px;
|
|
|
|
- margin-bottom: 20px;
|
|
|
|
- }
|
|
|
|
- .tag {
|
|
|
|
- background-color: #e0f2fe;
|
|
|
|
- color: #0369a1;
|
|
|
|
- padding: 5px 10px;
|
|
|
|
- border-radius: 15px;
|
|
|
|
- cursor: pointer;
|
|
|
|
- transition: all 0.2s;
|
|
|
|
- user-select: none;
|
|
|
|
- }
|
|
|
|
- .tag.active {
|
|
|
|
- background-color: #0284c7;
|
|
|
|
- color: white;
|
|
|
|
- }
|
|
|
|
- .tag:hover {
|
|
|
|
- opacity: 0.8;
|
|
|
|
- }
|
|
|
|
- input, button, select {
|
|
|
|
- padding: 8px 12px;
|
|
|
|
- border: 1px solid #ddd;
|
|
|
|
- border-radius: 4px;
|
|
|
|
- font-size: 14px;
|
|
|
|
- }
|
|
|
|
- input:focus, select:focus {
|
|
|
|
- outline: none;
|
|
|
|
- border-color: #0284c7;
|
|
|
|
- }
|
|
|
|
- button {
|
|
|
|
- background-color: #0284c7;
|
|
|
|
- color: white;
|
|
|
|
- cursor: pointer;
|
|
|
|
- border: none;
|
|
|
|
- }
|
|
|
|
- button:hover {
|
|
|
|
- background-color: #0369a1;
|
|
|
|
- }
|
|
|
|
- table {
|
|
|
|
- width: 100%;
|
|
|
|
- border-collapse: collapse;
|
|
|
|
- margin-top: 20px;
|
|
|
|
- background-color: white;
|
|
|
|
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
|
|
|
- border-radius: 8px;
|
|
|
|
- overflow: hidden;
|
|
|
|
- }
|
|
|
|
- th, td {
|
|
|
|
- padding: 12px 15px;
|
|
|
|
- text-align: left;
|
|
|
|
- border-bottom: 1px solid #eee;
|
|
|
|
- }
|
|
|
|
- th {
|
|
|
|
- background-color: #f1f5f9;
|
|
|
|
- font-weight: 600;
|
|
|
|
- color: #334155;
|
|
|
|
- }
|
|
|
|
- tr:hover {
|
|
|
|
- background-color: #f1f5f9;
|
|
|
|
- }
|
|
|
|
- .preview-img {
|
|
|
|
- max-width: 100px;
|
|
|
|
- max-height: 100px;
|
|
|
|
- object-fit: contain;
|
|
|
|
- cursor: pointer;
|
|
|
|
- }
|
|
|
|
- .modal {
|
|
|
|
- display: none;
|
|
|
|
- position: fixed;
|
|
|
|
- top: 0;
|
|
|
|
- left: 0;
|
|
|
|
- width: 100%;
|
|
|
|
- height: 100%;
|
|
|
|
- background-color: rgba(0,0,0,0.8);
|
|
|
|
- z-index: 1000;
|
|
|
|
- justify-content: center;
|
|
|
|
- align-items: center;
|
|
|
|
- }
|
|
|
|
- .modal-content {
|
|
|
|
- max-width: 90%;
|
|
|
|
- max-height: 90%;
|
|
|
|
- }
|
|
|
|
- .modal-content img {
|
|
|
|
- max-width: 100%;
|
|
|
|
- max-height: 90vh;
|
|
|
|
- object-fit: contain;
|
|
|
|
- }
|
|
|
|
- .close {
|
|
|
|
- position: absolute;
|
|
|
|
- top: 20px;
|
|
|
|
- right: 30px;
|
|
|
|
- color: white;
|
|
|
|
- font-size: 30px;
|
|
|
|
- cursor: pointer;
|
|
|
|
- }
|
|
|
|
- .export-import {
|
|
|
|
- margin-top: 20px;
|
|
|
|
- display: flex;
|
|
|
|
- gap: 10px;
|
|
|
|
- }
|
|
|
|
- #importInput {
|
|
|
|
- display: none;
|
|
|
|
- }
|
|
|
|
- </style>
|
|
|
|
-</head>
|
|
|
|
-<body>
|
|
|
|
-<div class="container">
|
|
|
|
- <div class="header">
|
|
|
|
- <h1>简易链接管理器</h1>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="toolbar">
|
|
|
|
- <input type="text" id="searchInput" placeholder="搜索...">
|
|
|
|
- <select id="sortSelect">
|
|
|
|
- <option value="dateDesc">日期 (新→旧)</option>
|
|
|
|
- <option value="dateAsc">日期 (旧→新)</option>
|
|
|
|
- <option value="titleAsc">标题 (A→Z)</option>
|
|
|
|
- <option value="titleDesc">标题 (Z→A)</option>
|
|
|
|
- </select>
|
|
|
|
- <button id="addNewBtn">添加新链接</button>
|
|
|
|
- </div>
|
|
|
|
-
|
|
|
|
- <div class="tag-list" id="tagList"></div>
|
|
|
|
-
|
|
|
|
- <table id="dataTable">
|
|
|
|
- <thead>
|
|
|
|
- <tr>
|
|
|
|
- <th>标题</th>
|
|
|
|
- <th>链接</th>
|
|
|
|
- <th>图片</th>
|
|
|
|
- <th>标签</th>
|
|
|
|
- <th>添加日期</th>
|
|
|
|
- <th>操作</th>
|
|
|
|
- </tr>
|
|
|
|
- </thead>
|
|
|
|
- <tbody id="tableBody"></tbody>
|
|
|
|
- </table>
|
|
|
|
-
|
|
|
|
- <div class="export-import">
|
|
|
|
- <button id="exportBtn">导出数据</button>
|
|
|
|
- <button id="importBtn">导入数据</button>
|
|
|
|
- <input type="file" id="importInput" accept=".json">
|
|
|
|
- </div>
|
|
|
|
-</div>
|
|
|
|
-
|
|
|
|
-<div id="imageModal" class="modal">
|
|
|
|
- <span class="close">×</span>
|
|
|
|
- <div class="modal-content">
|
|
|
|
- <img id="modalImage" src="">
|
|
|
|
- </div>
|
|
|
|
-</div>
|
|
|
|
-
|
|
|
|
-<script>
|
|
|
|
- // 初始数据存储
|
|
|
|
- let data = JSON.parse(localStorage.getItem('linkData')) || [];
|
|
|
|
- let allTags = new Set();
|
|
|
|
- let activeTagFilters = new Set();
|
|
|
|
-
|
|
|
|
- // 初始化应用
|
|
|
|
- function init() {
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- setupEventListeners();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 从数据中提取所有唯一标签
|
|
|
|
- function extractAllTags() {
|
|
|
|
- allTags = new Set();
|
|
|
|
- data.forEach(item => {
|
|
|
|
- if (item.tags && Array.isArray(item.tags)) {
|
|
|
|
- item.tags.forEach(tag => allTags.add(tag));
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- return Array.from(allTags).sort();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 渲染标签筛选器
|
|
|
|
- function renderTags() {
|
|
|
|
- const tags = extractAllTags();
|
|
|
|
- const tagList = document.getElementById('tagList');
|
|
|
|
- tagList.innerHTML = '';
|
|
|
|
-
|
|
|
|
- tags.forEach(tag => {
|
|
|
|
- const tagElement = document.createElement('span');
|
|
|
|
- tagElement.className = `tag ${activeTagFilters.has(tag) ? 'active' : ''}`;
|
|
|
|
- tagElement.textContent = tag;
|
|
|
|
- tagElement.dataset.tag = tag;
|
|
|
|
- tagList.appendChild(tagElement);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 渲染数据表格
|
|
|
|
- function renderTable() {
|
|
|
|
- const tableBody = document.getElementById('tableBody');
|
|
|
|
- tableBody.innerHTML = '';
|
|
|
|
-
|
|
|
|
- const searchTerm = document.getElementById('searchInput').value.toLowerCase();
|
|
|
|
- const sortMethod = document.getElementById('sortSelect').value;
|
|
|
|
-
|
|
|
|
- // 筛选数据
|
|
|
|
- let filteredData = data.filter(item => {
|
|
|
|
- // 标签筛选
|
|
|
|
- if (activeTagFilters.size > 0) {
|
|
|
|
- if (!item.tags || !Array.isArray(item.tags)) return false;
|
|
|
|
- const hasAllTags = Array.from(activeTagFilters).every(tag =>
|
|
|
|
- item.tags.includes(tag)
|
|
|
|
- );
|
|
|
|
- if (!hasAllTags) return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 搜索筛选
|
|
|
|
- if (searchTerm) {
|
|
|
|
- const searchFields = [
|
|
|
|
- item.title,
|
|
|
|
- item.url,
|
|
|
|
- (item.tags || []).join(' ')
|
|
|
|
- ].join(' ').toLowerCase();
|
|
|
|
-
|
|
|
|
- return searchFields.includes(searchTerm);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return true;
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 排序数据
|
|
|
|
- filteredData.sort((a, b) => {
|
|
|
|
- switch (sortMethod) {
|
|
|
|
- case 'dateDesc':
|
|
|
|
- return new Date(b.date) - new Date(a.date);
|
|
|
|
- case 'dateAsc':
|
|
|
|
- return new Date(a.date) - new Date(b.date);
|
|
|
|
- case 'titleAsc':
|
|
|
|
- return a.title.localeCompare(b.title);
|
|
|
|
- case 'titleDesc':
|
|
|
|
- return b.title.localeCompare(a.title);
|
|
|
|
- default:
|
|
|
|
- return new Date(b.date) - new Date(a.date);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 渲染数据行
|
|
|
|
- filteredData.forEach((item, index) => {
|
|
|
|
- const row = document.createElement('tr');
|
|
|
|
-
|
|
|
|
- // 标题列
|
|
|
|
- const titleCell = document.createElement('td');
|
|
|
|
- titleCell.textContent = item.title;
|
|
|
|
- row.appendChild(titleCell);
|
|
|
|
-
|
|
|
|
- // 链接列
|
|
|
|
- const urlCell = document.createElement('td');
|
|
|
|
- const urlLink = document.createElement('a');
|
|
|
|
- urlLink.href = item.url;
|
|
|
|
- urlLink.textContent = item.url.length > 30 ? item.url.substring(0, 30) + '...' : item.url;
|
|
|
|
- urlLink.target = '_blank';
|
|
|
|
- urlCell.appendChild(urlLink);
|
|
|
|
- row.appendChild(urlCell);
|
|
|
|
-
|
|
|
|
- // 图片列
|
|
|
|
- const imageCell = document.createElement('td');
|
|
|
|
- if (item.imageUrl) {
|
|
|
|
- const img = document.createElement('img');
|
|
|
|
- img.src = item.imageUrl;
|
|
|
|
- img.alt = item.title;
|
|
|
|
- img.className = 'preview-img';
|
|
|
|
- img.dataset.fullImage = item.imageUrl;
|
|
|
|
- imageCell.appendChild(img);
|
|
|
|
- }
|
|
|
|
- row.appendChild(imageCell);
|
|
|
|
-
|
|
|
|
- // 标签列
|
|
|
|
- const tagsCell = document.createElement('td');
|
|
|
|
- if (item.tags && Array.isArray(item.tags)) {
|
|
|
|
- item.tags.forEach(tag => {
|
|
|
|
- const tagSpan = document.createElement('span');
|
|
|
|
- tagSpan.className = 'tag';
|
|
|
|
- tagSpan.textContent = tag;
|
|
|
|
- tagsCell.appendChild(tagSpan);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- row.appendChild(tagsCell);
|
|
|
|
-
|
|
|
|
- // 日期列
|
|
|
|
- const dateCell = document.createElement('td');
|
|
|
|
- dateCell.textContent = new Date(item.date).toLocaleDateString();
|
|
|
|
- row.appendChild(dateCell);
|
|
|
|
-
|
|
|
|
- // 操作列
|
|
|
|
- const actionsCell = document.createElement('td');
|
|
|
|
- const editBtn = document.createElement('button');
|
|
|
|
- editBtn.textContent = '编辑';
|
|
|
|
- editBtn.dataset.index = index;
|
|
|
|
- editBtn.style.marginRight = '5px';
|
|
|
|
-
|
|
|
|
- const deleteBtn = document.createElement('button');
|
|
|
|
- deleteBtn.textContent = '删除';
|
|
|
|
- deleteBtn.dataset.index = index;
|
|
|
|
- deleteBtn.style.backgroundColor = '#ef4444';
|
|
|
|
-
|
|
|
|
- actionsCell.appendChild(editBtn);
|
|
|
|
- actionsCell.appendChild(deleteBtn);
|
|
|
|
- row.appendChild(actionsCell);
|
|
|
|
-
|
|
|
|
- tableBody.appendChild(row);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 设置事件监听器
|
|
|
|
- function setupEventListeners() {
|
|
|
|
- // 搜索框变化时更新表格
|
|
|
|
- document.getElementById('searchInput').addEventListener('input', renderTable);
|
|
|
|
-
|
|
|
|
- // 排序选择变化时更新表格
|
|
|
|
- document.getElementById('sortSelect').addEventListener('change', renderTable);
|
|
|
|
-
|
|
|
|
- // 标签点击事件
|
|
|
|
- document.getElementById('tagList').addEventListener('click', event => {
|
|
|
|
- if (event.target.classList.contains('tag')) {
|
|
|
|
- const tag = event.target.dataset.tag;
|
|
|
|
- if (activeTagFilters.has(tag)) {
|
|
|
|
- activeTagFilters.delete(tag);
|
|
|
|
- } else {
|
|
|
|
- activeTagFilters.add(tag);
|
|
|
|
- }
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 添加新链接按钮
|
|
|
|
- document.getElementById('addNewBtn').addEventListener('click', () => {
|
|
|
|
- const title = prompt('输入标题:');
|
|
|
|
- if (!title) return;
|
|
|
|
-
|
|
|
|
- const url = prompt('输入URL:');
|
|
|
|
- if (!url) return;
|
|
|
|
-
|
|
|
|
- const imageUrl = prompt('输入图片URL (可选):');
|
|
|
|
-
|
|
|
|
- const tagsInput = prompt('输入标签 (用逗号分隔):');
|
|
|
|
- const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
|
|
-
|
|
|
|
- const newItem = {
|
|
|
|
- title,
|
|
|
|
- url,
|
|
|
|
- imageUrl,
|
|
|
|
- tags,
|
|
|
|
- date: new Date().toISOString()
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- data.push(newItem);
|
|
|
|
- saveData();
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 表格行操作按钮
|
|
|
|
- document.getElementById('tableBody').addEventListener('click', event => {
|
|
|
|
- if (event.target.tagName === 'BUTTON') {
|
|
|
|
- const index = parseInt(event.target.dataset.index);
|
|
|
|
- const item = data[index];
|
|
|
|
-
|
|
|
|
- if (event.target.textContent === '编辑') {
|
|
|
|
- // 编辑操作
|
|
|
|
- const title = prompt('编辑标题:', item.title);
|
|
|
|
- if (!title) return;
|
|
|
|
-
|
|
|
|
- const url = prompt('编辑URL:', item.url);
|
|
|
|
- if (!url) return;
|
|
|
|
-
|
|
|
|
- const imageUrl = prompt('编辑图片URL (可选):', item.imageUrl || '');
|
|
|
|
-
|
|
|
|
- const tagsInput = prompt('编辑标签 (用逗号分隔):', (item.tags || []).join(', '));
|
|
|
|
- const tags = tagsInput ? tagsInput.split(',').map(t => t.trim()).filter(t => t) : [];
|
|
|
|
-
|
|
|
|
- data[index] = {
|
|
|
|
- ...item,
|
|
|
|
- title,
|
|
|
|
- url,
|
|
|
|
- imageUrl,
|
|
|
|
- tags
|
|
|
|
- };
|
|
|
|
-
|
|
|
|
- saveData();
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- } else if (event.target.textContent === '删除') {
|
|
|
|
- // 删除操作
|
|
|
|
- if (confirm(`确定要删除 "${item.title}" 吗?`)) {
|
|
|
|
- data.splice(index, 1);
|
|
|
|
- saveData();
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else if (event.target.classList.contains('preview-img')) {
|
|
|
|
- // 图片预览
|
|
|
|
- const modal = document.getElementById('imageModal');
|
|
|
|
- const modalImg = document.getElementById('modalImage');
|
|
|
|
- modal.style.display = 'flex';
|
|
|
|
- modalImg.src = event.target.dataset.fullImage;
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 图片模态框关闭按钮
|
|
|
|
- document.querySelector('.close').addEventListener('click', () => {
|
|
|
|
- document.getElementById('imageModal').style.display = 'none';
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 导出数据
|
|
|
|
- document.getElementById('exportBtn').addEventListener('click', () => {
|
|
|
|
- const dataStr = JSON.stringify(data, null, 2);
|
|
|
|
- const blob = new Blob([dataStr], {type: 'application/json'});
|
|
|
|
- const url = URL.createObjectURL(blob);
|
|
|
|
- const a = document.createElement('a');
|
|
|
|
- a.href = url;
|
|
|
|
- a.download = `bookmark-data-${new Date().toISOString().slice(0,10)}.json`;
|
|
|
|
- document.body.appendChild(a);
|
|
|
|
- a.click();
|
|
|
|
- document.body.removeChild(a);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 导入数据点击
|
|
|
|
- document.getElementById('importBtn').addEventListener('click', () => {
|
|
|
|
- document.getElementById('importInput').click();
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- // 导入数据处理
|
|
|
|
- document.getElementById('importInput').addEventListener('change', event => {
|
|
|
|
- const file = event.target.files[0];
|
|
|
|
- if (!file) return;
|
|
|
|
-
|
|
|
|
- const reader = new FileReader();
|
|
|
|
- reader.onload = function(e) {
|
|
|
|
- try {
|
|
|
|
- const importedData = JSON.parse(e.target.result);
|
|
|
|
- if (Array.isArray(importedData)) {
|
|
|
|
- if (confirm(`确定要导入 ${importedData.length} 条数据吗? 这将覆盖当前数据。`)) {
|
|
|
|
- data = importedData;
|
|
|
|
- saveData();
|
|
|
|
- renderTags();
|
|
|
|
- renderTable();
|
|
|
|
- alert('数据导入成功!');
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- alert('导入的文件格式不正确。请提供有效的JSON数组。');
|
|
|
|
- }
|
|
|
|
- } catch (err) {
|
|
|
|
- alert('导入失败: ' + err.message);
|
|
|
|
- }
|
|
|
|
- event.target.value = '';
|
|
|
|
- };
|
|
|
|
- reader.readAsText(file);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 保存数据到localStorage
|
|
|
|
- function saveData() {
|
|
|
|
- localStorage.setItem('linkData', JSON.stringify(data));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 初始化应用
|
|
|
|
- init();
|
|
|
|
-</script>
|
|
|
|
-</body>
|
|
|
|
|
|
+<!DOCTYPE html>
|
|
|
|
+<html>
|
|
|
|
+<head>
|
|
|
|
+ <style>
|
|
|
|
+ /* 隐藏原生 video 控件,用 Canvas 展示动画 */
|
|
|
|
+ #audioPlayer { display: none; }
|
|
|
|
+ body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #000; }
|
|
|
|
+ </style>
|
|
|
|
+</head>
|
|
|
|
+<body>
|
|
|
|
+
|
|
|
|
+<input type="button" onclick="audio.play()" value="播放" />
|
|
|
|
+<input type="button" onclick="audio.pause()" value="暂停" />
|
|
|
|
+<canvas id="wrap" height="550" width="800"></canvas>
|
|
|
|
+<script>
|
|
|
|
+ var wrap = document.getElementById("wrap");
|
|
|
|
+ var cxt = wrap.getContext("2d");
|
|
|
|
+ //获取API
|
|
|
|
+ var AudioContext = AudioContext || webkitAudioContext;
|
|
|
|
+ var context = new AudioContext;
|
|
|
|
+ //加载媒体
|
|
|
|
+ var audio = new Audio("http://192.168.3.36:5002/play/5e25571cae884d307e0a8f576fbc8632?t=1743822135234");
|
|
|
|
+ //创建节点
|
|
|
|
+ var source = context.createMediaElementSource(audio);
|
|
|
|
+ var analyser = context.createAnalyser();
|
|
|
|
+ //连接:source → analyser → destination
|
|
|
|
+ source.connect(analyser);
|
|
|
|
+ analyser.connect(context.destination);
|
|
|
|
+ //创建数据
|
|
|
|
+ var output = new Uint8Array(361);
|
|
|
|
+ //计算出采样频率44100所需的缓冲区长度
|
|
|
|
+ var length = analyser.frequencyBinCount * 44100 / context.sampleRate | 0;
|
|
|
|
+ //创建数据
|
|
|
|
+ var output2 = new Uint8Array(length);
|
|
|
|
+ (function drawSpectrum() {
|
|
|
|
+ analyser.getByteFrequencyData(output);//获取频域数据
|
|
|
|
+ cxt.clearRect(0, 0, wrap.width, wrap.height);
|
|
|
|
+ //画线条
|
|
|
|
+ for (var i = 0; i < output.length; i++) {
|
|
|
|
+ var value = output[i] / 10;
|
|
|
|
+ //绘制左半边
|
|
|
|
+ cxt.beginPath();
|
|
|
|
+ cxt.lineWidth = 1;
|
|
|
|
+ cxt.moveTo(300, 300);
|
|
|
|
+ cxt.lineTo(Math.cos((i * 0.5 + 90) / 180 * Math.PI) * (200 + value) + 300, (- Math.sin((i * 0.5 + 90) / 180 * Math.PI) * (200 + value) + 300));
|
|
|
|
+ cxt.stroke();
|
|
|
|
+ //绘制右半边
|
|
|
|
+ cxt.beginPath();
|
|
|
|
+ cxt.lineWidth = 1;
|
|
|
|
+ cxt.moveTo(300, 300);
|
|
|
|
+ cxt.lineTo((Math.sin((i * 0.5) / 180 * Math.PI) * (200 + value) + 300), -Math.cos((i * 0.5) / 180 * Math.PI) * (200 + value) + 300);
|
|
|
|
+ cxt.stroke();
|
|
|
|
+ }
|
|
|
|
+ //画一个小圆,将线条覆盖
|
|
|
|
+ cxt.beginPath();
|
|
|
|
+ cxt.lineWidth = 1;
|
|
|
|
+ cxt.arc(300, 300, 200, 0, 2 * Math.PI, false);
|
|
|
|
+ cxt.fillStyle = "#fff";
|
|
|
|
+ cxt.stroke();
|
|
|
|
+ cxt.fill();
|
|
|
|
+ //将缓冲区的数据绘制到Canvas上
|
|
|
|
+ analyser.getByteTimeDomainData(output2);
|
|
|
|
+ var height = 100, width = 400;
|
|
|
|
+ cxt.beginPath();
|
|
|
|
+ for (var i = 0; i < width; i++) {
|
|
|
|
+ cxt.lineTo(i + 100, 300 - (height / 2 * (output2[output2.length * i / width | 0] / 256 - 0.5)));
|
|
|
|
+ }
|
|
|
|
+ cxt.stroke();
|
|
|
|
+ //请求下一帧
|
|
|
|
+ requestAnimationFrame(drawSpectrum);
|
|
|
|
+ })();
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+</body>
|
|
</html>
|
|
</html>
|