文章目录[隐藏]
详细教程:为WordPress网站打造内嵌在线简易动画制作与GIF生成工具
引言:为什么网站需要内置动画制作工具?
在当今数字时代,视觉内容已成为网站吸引用户、提升参与度的关键因素。动画和GIF图像因其生动、直观的表达方式,在社交媒体、产品演示、教程说明等场景中发挥着不可替代的作用。然而,对于大多数网站运营者来说,创建这些视觉元素通常需要依赖外部工具或专业设计人员,这不仅增加了成本,也降低了内容创作的灵活性。
通过为WordPress网站开发一个内置的简易动画制作与GIF生成工具,您可以:
- 大幅降低视觉内容创作门槛
- 提高内容生产效率
- 保持品牌视觉一致性
- 增强用户互动体验
- 减少对外部服务的依赖
本教程将详细指导您如何通过WordPress代码二次开发,实现这一实用功能,让您的网站具备专业级的简易动画制作能力。
第一部分:准备工作与环境配置
1.1 开发环境搭建
在开始开发之前,确保您已具备以下条件:
- 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境
- 代码编辑器:Visual Studio Code、Sublime Text或PHPStorm
- WordPress版本:5.0或更高版本
- 基础技能:HTML、CSS、JavaScript、PHP基础知识和WordPress主题开发经验
1.2 创建开发专用子主题
为避免影响主主题功能,建议创建专用子主题:
/*
Theme Name: Animation Tools Child Theme
Template: your-parent-theme-folder-name
Version: 1.0
*/
// 引入父主题样式表
add_action('wp_enqueue_scripts', 'animation_tools_enqueue_styles');
function animation_tools_enqueue_styles() {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
wp_enqueue_style('child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style'));
}
1.3 创建必要的目录结构
在子主题目录中创建以下结构:
your-child-theme/
├── animation-tools/
│ ├── css/
│ │ └── animation-editor.css
│ ├── js/
│ │ ├── animation-editor.js
│ │ └── gif-generator.js
│ └── lib/
│ └── gif.js
├── templates/
│ └── animation-tool-page.php
└── functions.php
第二部分:构建动画编辑器界面
2.1 创建动画工具管理页面
首先,在WordPress后台添加一个管理页面:
// 在functions.php中添加
add_action('admin_menu', 'animation_tools_admin_menu');
function animation_tools_admin_menu() {
add_menu_page(
'动画制作工具',
'动画工具',
'manage_options',
'animation-tools',
'animation_tools_admin_page',
'dashicons-video-alt3',
30
);
}
function animation_tools_admin_page() {
?>
<div class="wrap">
<h1>网站动画制作工具</h1>
<div id="animation-tools-admin">
<p>从这里可以访问网站内置的动画编辑器。</p>
<a href="<?php echo home_url('/animation-editor/'); ?>" class="button button-primary" target="_blank">打开动画编辑器</a>
</div>
</div>
<?php
}
2.2 创建前端动画编辑器页面模板
创建自定义页面模板,用于前端动画编辑:
<?php
/*
Template Name: 动画编辑器
*/
get_header(); ?>
<div class="animation-editor-container">
<div class="editor-header">
<h1>简易动画制作工具</h1>
<div class="editor-tabs">
<button class="tab-btn active" data-tab="canvas-editor">画布编辑</button>
<button class="tab-btn" data-tab="timeline">时间轴</button>
<button class="tab-btn" data-tab="export">导出选项</button>
</div>
</div>
<div class="editor-main">
<div class="tool-panel">
<div class="tool-section">
<h3>绘图工具</h3>
<div class="tool-buttons">
<button class="tool-btn active" data-tool="select">选择</button>
<button class="tool-btn" data-tool="brush">画笔</button>
<button class="tool-btn" data-tool="shape">形状</button>
<button class="tool-btn" data-tool="text">文字</button>
<button class="tool-btn" data-tool="eraser">橡皮擦</button>
</div>
</div>
<div class="tool-section">
<h3>属性设置</h3>
<div class="property-controls">
<div class="control-group">
<label>画笔大小:</label>
<input type="range" id="brush-size" min="1" max="50" value="5">
<span id="brush-size-value">5px</span>
</div>
<div class="control-group">
<label>颜色:</label>
<input type="color" id="brush-color" value="#ff0000">
</div>
<div class="control-group">
<label>不透明度:</label>
<input type="range" id="brush-opacity" min="0" max="100" value="100">
<span id="opacity-value">100%</span>
</div>
</div>
</div>
</div>
<div class="canvas-area">
<div class="canvas-container">
<canvas id="animation-canvas" width="800" height="600"></canvas>
<div class="canvas-overlay">
<div class="canvas-grid"></div>
</div>
</div>
<div class="canvas-controls">
<button id="clear-canvas">清空画布</button>
<button id="undo-action">撤销</button>
<button id="redo-action">重做</button>
<button id="add-frame">添加帧</button>
</div>
</div>
<div class="timeline-panel">
<div class="frames-container">
<div class="frames-list" id="frames-list">
<!-- 帧缩略图将通过JS动态生成 -->
</div>
<div class="timeline-controls">
<button id="play-animation">播放</button>
<input type="range" id="frame-speed" min="1" max="30" value="12">
<span>帧速率: <span id="fps-value">12</span> fps</span>
</div>
</div>
</div>
</div>
<div class="export-panel">
<h3>导出选项</h3>
<div class="export-options">
<div class="export-format">
<label><input type="radio" name="export-format" value="gif" checked> GIF动画</label>
<label><input type="radio" name="export-format" value="apng"> APNG</label>
<label><input type="radio" name="export-format" value="spritesheet"> 精灵图</label>
</div>
<div class="export-settings">
<div class="setting-group">
<label>循环次数:</label>
<select id="loop-count">
<option value="0">无限循环</option>
<option value="1">1次</option>
<option value="3">3次</option>
<option value="5">5次</option>
</select>
</div>
<div class="setting-group">
<label>质量:</label>
<input type="range" id="export-quality" min="1" max="100" value="80">
<span id="quality-value">80%</span>
</div>
</div>
<div class="export-actions">
<button id="preview-export">预览</button>
<button id="generate-export" class="button-primary">生成并下载</button>
<button id="save-to-media">保存到媒体库</button>
</div>
</div>
</div>
</div>
<?php get_footer(); ?>
第三部分:实现动画编辑器核心功能
3.1 动画编辑器JavaScript核心
创建动画编辑器的主要JavaScript功能:
// animation-editor.js
document.addEventListener('DOMContentLoaded', function() {
// 初始化变量
const canvas = document.getElementById('animation-canvas');
const ctx = canvas.getContext('2d');
const framesList = document.getElementById('frames-list');
let currentTool = 'brush';
let brushSize = 5;
let brushColor = '#ff0000';
let brushOpacity = 1.0;
let frames = [];
let currentFrameIndex = 0;
let isDrawing = false;
let lastX = 0;
let lastY = 0;
let undoStack = [];
let redoStack = [];
// 初始化第一帧
function initializeFirstFrame() {
const frameData = {
id: Date.now(),
imageData: null,
thumbnail: null,
delay: 100 // 默认每帧延迟100ms
};
frames.push(frameData);
updateFrameDisplay();
saveFrameState();
}
// 保存当前帧状态
function saveFrameState() {
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
frames[currentFrameIndex].imageData = imageData;
// 创建缩略图
const thumbnailCanvas = document.createElement('canvas');
thumbnailCanvas.width = 80;
thumbnailCanvas.height = 60;
const thumbnailCtx = thumbnailCanvas.getContext('2d');
thumbnailCtx.drawImage(canvas, 0, 0, 80, 60);
frames[currentFrameIndex].thumbnail = thumbnailCanvas.toDataURL();
updateFrameDisplay();
}
// 更新帧显示
function updateFrameDisplay() {
framesList.innerHTML = '';
frames.forEach((frame, index) => {
const frameElement = document.createElement('div');
frameElement.className = `frame-thumb ${index === currentFrameIndex ? 'active' : ''}`;
frameElement.innerHTML = `
<div class="frame-number">${index + 1}</div>
<img src="${frame.thumbnail || ''}" alt="帧 ${index + 1}">
<div class="frame-actions">
<button class="frame-delete" data-index="${index}">删除</button>
</div>
`;
frameElement.addEventListener('click', () => {
switchToFrame(index);
});
framesList.appendChild(frameElement);
});
}
// 切换到指定帧
function switchToFrame(index) {
saveFrameState(); // 保存当前帧
currentFrameIndex = index;
// 恢复帧内容
const frame = frames[index];
if (frame.imageData) {
ctx.putImageData(frame.imageData, 0, 0);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
updateFrameDisplay();
}
// 添加新帧
document.getElementById('add-frame').addEventListener('click', function() {
saveFrameState();
const newFrame = {
id: Date.now(),
imageData: null,
thumbnail: null,
delay: 100
};
frames.splice(currentFrameIndex + 1, 0, newFrame);
currentFrameIndex++;
// 清空画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateFrameDisplay();
saveFrameState();
});
// 绘图功能
function startDrawing(e) {
if (currentTool === 'select') return;
isDrawing = true;
[lastX, lastY] = getMousePos(canvas, e);
// 开始新路径
if (currentTool === 'brush' || currentTool === 'eraser') {
ctx.beginPath();
ctx.moveTo(lastX, lastY);
}
}
function draw(e) {
if (!isDrawing) return;
const [x, y] = getMousePos(canvas, e);
switch(currentTool) {
case 'brush':
ctx.lineTo(x, y);
ctx.strokeStyle = brushColor;
ctx.lineWidth = brushSize;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.globalAlpha = brushOpacity;
ctx.stroke();
break;
case 'eraser':
ctx.lineTo(x, y);
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = brushSize;
ctx.lineCap = 'round';
ctx.stroke();
break;
case 'shape':
// 绘制形状逻辑
break;
case 'text':
// 文本输入逻辑
break;
}
[lastX, lastY] = [x, y];
}
function stopDrawing() {
if (!isDrawing) return;
isDrawing = false;
ctx.closePath();
saveFrameState();
}
// 获取鼠标位置
function getMousePos(canvas, evt) {
const rect = canvas.getBoundingClientRect();
return [
evt.clientX - rect.left,
evt.clientY - rect.top
];
}
// 工具选择
document.querySelectorAll('.tool-btn').forEach(btn => {
btn.addEventListener('click', function() {
document.querySelectorAll('.tool-btn').forEach(b => b.classList.remove('active'));
this.classList.add('active');
currentTool = this.dataset.tool;
});
});
// 属性控制
document.getElementById('brush-size').addEventListener('input', function() {
brushSize = this.value;
document.getElementById('brush-size-value').textContent = brushSize + 'px';
});
document.getElementById('brush-color').addEventListener('input', function() {
brushColor = this.value;
});
document.getElementById('brush-opacity').addEventListener('input', function() {
brushOpacity = this.value / 100;
document.getElementById('opacity-value').textContent = this.value + '%';
});
// 画布事件监听
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('mouseout', stopDrawing);
// 清空画布
document.getElementById('clear-canvas').addEventListener('click', function() {
if (confirm('确定要清空当前帧吗?')) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
saveFrameState();
}
});
// 初始化
initializeFirstFrame();
});
3.2 添加动画播放功能
// 在animation-editor.js中添加动画播放功能
let animationInterval = null;
let isPlaying = false;
function playAnimation() {
if (frames.length < 2) {
alert('至少需要两帧才能播放动画');
return;
}
if (isPlaying) {
stopAnimation();
return;
}
isPlaying = true;
document.getElementById('play-animation').textContent = '停止';
let frameIndex = 0;
const fps = parseInt(document.getElementById('frame-speed').value);
const delay = 1000 / fps;
animationInterval = setInterval(() => {
switchToFrame(frameIndex);
frameIndex = (frameIndex + 1) % frames.length;
}, delay);
}
function stopAnimation() {
isPlaying = false;
document.getElementById('play-animation').textContent = '播放';
clearInterval(animationInterval);
}
// 播放按钮事件
document.getElementById('play-animation').addEventListener('click', playAnimation);
// 帧速率控制
document.getElementById('frame-speed').addEventListener('input', function() {
document.getElementById('fps-value').textContent = this.value;
// 如果正在播放,重新开始以应用新速度
if (isPlaying) {
stopAnimation();
playAnimation();
}
});
第四部分:实现GIF生成与导出功能
4.1 集成GIF.js库
首先,下载并引入GIF.js库:
// 在functions.php中注册GIF.js
add_action('wp_enqueue_scripts', 'enqueue_animation_tools_scripts');
function enqueue_animation_tools_scripts() {
if (is_page_template('animation-editor.php')) {
wp_enqueue_script('gif-js', get_stylesheet_directory_uri() . '/animation-tools/lib/gif.js', array(), '1.0.0', true);
wp_enqueue_script('animation-editor', get_stylesheet_directory_uri() . '/animation-tools/js/animation-editor.js', array('jquery', 'gif-js'), '1.0.0', true);
wp_enqueue_script('gif-generator', get_stylesheet_directory_uri() . '/animation-tools/js/gif-generator.js', array('animation-editor'), '1.0.0', true);
wp_enqueue_style('animation-editor-style', get_stylesheet_directory_uri() . '/animation-tools/css/animation-editor.css', array(), '1.0.0');
}
}
4.2 创建GIF生成器
// gif-generator.js
class GIFGenerator {
constructor(options = {}) {
this.options = {
quality: options.quality || 10,
width: options.width || 800,
height: options.height || 600,
workerScript: options.workerScript || '/wp-content/themes/your-child-theme/animation-tools/lib/gif.worker.js'
};
this.gif = new GIF({
workers: 2,
quality: this.options.quality,
width: this.options.width,
height: this.options.height,
workerScript: this.options.workerScript
});
this.frames = [];
this.onProgress = null;
this.onFinished = null;
}
addFrame(canvas, delay) {
this.gif.addFrame(canvas, {
delay: delay || 100,
copy: true
});
}
setOptions(options) {
if (options.quality) this.gif.setOption('quality', options.quality);
if (options.repeat !== undefined) this.gif.setOption('repeat', options.repeat);
}
generate() {
return new Promise((resolve, reject) => {
this.gif.on('progress', (progress) => {
if (this.onProgress) {
this.onProgress(progress);
}
});
this.gif.on('finished', (blob) => {
if (this.onFinished) {
this.onFinished(blob);
}
resolve(blob);
});
this.gif.render();
});
}
abort() {
this.gif.abort();
}
}
// 导出功能实现
document.addEventListener('DOMContentLoaded', function() {
const generateExportBtn = document.getElementById('generate-export');
const previewExportBtn = document.getElementById('preview-export');
const saveToMediaBtn = document.getElementById('save-to-media');
const exportQuality = document.getElementById('export-quality');
const loopCount = document.getElementById('loop-count');
let currentGIFBlob = null;
// 更新质量显示
exportQuality.addEventListener('input', function() {
document.getElementById('quality-value').textContent = this.value + '%';
});
// 生成GIF
async function generateGIF() {
if (frames.length === 0) {
alert('请先创建至少一帧动画');
return;
}
generateExportBtn.disabled = true;
generateExportBtn.textContent = '生成中...';
try {
// 创建临时画布用于生成GIF
const tempCanvas = document.createElement('canvas');
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
const tempCtx = tempCanvas.getContext('2d');
// 初始化GIF生成器
const gifGenerator = new GIFGenerator({
quality: parseInt(exportQuality.value),
width: canvas.width,
height: canvas.height
});
// 设置循环次数
const repeat = parseInt(loopCount.value);
gifGenerator.setOptions({ repeat: repeat === 0 ? 0 : repeat - 1 });
// 添加进度监听
gifGenerator.onProgress = (progress) => {
console.log(`生成进度: ${Math.round(progress * 100)}%`);
};
// 添加所有帧
for (let i = 0; i < frames.length; i++) {
const frame = frames[i];
if (frame.imageData) {
tempCtx.putImageData(frame.imageData, 0, 0);
gifGenerator.addFrame(tempCanvas, frame.delay);
}
}
// 生成GIF
currentGIFBlob = await gifGenerator.generate();
// 创建下载链接
const url = URL.createObjectURL(currentGIFBlob);
const a = document.createElement('a');
a.href = url;
a.download = `animation-${Date.now()}.gif`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert('GIF生成完成并已开始下载!');
} catch (error) {
console.error('GIF生成失败:', error);
alert('GIF生成失败,请重试');
} finally {
generateExportBtn.disabled = false;
generateExportBtn.textContent = '生成并下载';
}
}
// 预览GIF
async function previewGIF() {
if (frames.length === 0) {
alert('请先创建至少一帧动画');
return;
}
previewExportBtn.disabled = true;
previewExportBtn.textContent = '预览生成中...';
try {
// 创建临时画布
const tempCanvas = document.createElement('canvas');
tempCanvas.width = 400; // 预览尺寸较小
tempCanvas.height = 300;
const tempCtx = tempCanvas.getContext('2d');
// 创建预览GIF生成器
const previewGenerator = new GIFGenerator({
quality: 20, // 预览质量较低
width: 400,
height: 300
});
// 添加所有帧(缩放)
for (let i = 0; i < frames.length; i++) {
const frame = frames[i];
if (frame.imageData) {
// 创建临时画布绘制并缩放
const frameCanvas = document.createElement('canvas');
frameCanvas.width = canvas.width;
frameCanvas.height = canvas.height;
const frameCtx = frameCanvas.getContext('2d');
frameCtx.putImageData(frame.imageData, 0, 0);
// 缩放到预览尺寸
tempCtx.clearRect(0, 0, 400, 300);
tempCtx.drawImage(frameCanvas, 0, 0, 400, 300);
previewGenerator.addFrame(tempCanvas, frame.delay);
}
}
// 生成预览GIF
const previewBlob = await previewGenerator.generate();
const previewUrl = URL.createObjectURL(previewBlob);
// 显示预览
showPreviewModal(previewUrl);
} catch (error) {
console.error('预览生成失败:', error);
alert('预览生成失败');
} finally {
previewExportBtn.disabled = false;
previewExportBtn.textContent = '预览';
}
}
// 显示预览模态框
function showPreviewModal(gifUrl) {
// 移除现有模态框
const existingModal = document.querySelector('.preview-modal');
if (existingModal) {
existingModal.remove();
}
// 创建模态框
const modal = document.createElement('div');
modal.className = 'preview-modal';
modal.innerHTML = `
<div class="preview-modal-content">
<div class="preview-modal-header">
<h3>GIF预览</h3>
<button class="close-preview">×</button>
</div>
<div class="preview-modal-body">
<img src="${gifUrl}" alt="GIF预览" class="preview-gif">
<div class="preview-info">
<p>尺寸: 400x300 (预览)</p>
<p>帧数: ${frames.length}</p>
<p>文件大小: ${Math.round(currentGIFBlob?.size / 1024) || '未知'} KB</p>
</div>
</div>
<div class="preview-modal-footer">
<button id="download-preview">下载预览</button>
<button id="use-full-quality">使用高质量生成</button>
</div>
</div>
`;
document.body.appendChild(modal);
// 关闭按钮事件
modal.querySelector('.close-preview').addEventListener('click', function() {
modal.remove();
URL.revokeObjectURL(gifUrl);
});
// 点击外部关闭
modal.addEventListener('click', function(e) {
if (e.target === modal) {
modal.remove();
URL.revokeObjectURL(gifUrl);
}
});
// 下载预览
modal.querySelector('#download-preview').addEventListener('click', function() {
const a = document.createElement('a');
a.href = gifUrl;
a.download = `preview-${Date.now()}.gif`;
a.click();
});
// 使用高质量生成
modal.querySelector('#use-full-quality').addEventListener('click', function() {
modal.remove();
URL.revokeObjectURL(gifUrl);
generateGIF();
});
}
// 保存到媒体库
async function saveToMediaLibrary() {
if (!currentGIFBlob) {
alert('请先生成GIF');
return;
}
saveToMediaBtn.disabled = true;
saveToMediaBtn.textContent = '上传中...';
try {
// 创建FormData
const formData = new FormData();
formData.append('action', 'save_animation_to_media');
formData.append('security', animationToolsAjax.nonce);
formData.append('gif_data', currentGIFBlob, `animation-${Date.now()}.gif`);
formData.append('title', `动画作品 ${new Date().toLocaleDateString()}`);
// 发送AJAX请求
const response = await fetch(animationToolsAjax.ajax_url, {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
alert(`动画已保存到媒体库!n文件ID: ${result.data.attachment_id}n查看: ${result.data.edit_link}`);
} else {
throw new Error(result.data || '上传失败');
}
} catch (error) {
console.error('保存到媒体库失败:', error);
alert('保存失败: ' + error.message);
} finally {
saveToMediaBtn.disabled = false;
saveToMediaBtn.textContent = '保存到媒体库';
}
}
// 绑定事件
generateExportBtn.addEventListener('click', generateGIF);
previewExportBtn.addEventListener('click', previewGIF);
saveToMediaBtn.addEventListener('click', saveToMediaLibrary);
});
## 第五部分:后端处理与媒体库集成
### 5.1 创建AJAX处理函数
// 在functions.php中添加AJAX处理
add_action('wp_ajax_save_animation_to_media', 'handle_save_animation_to_media');
add_action('wp_ajax_nopriv_save_animation_to_media', 'handle_save_animation_to_media');
function handle_save_animation_to_media() {
// 安全检查
check_ajax_referer('animation_tools_nonce', 'security');
// 验证用户权限
if (!current_user_can('upload_files')) {
wp_die('权限不足');
}
// 检查文件上传
if (!isset($_FILES['gif_data']) || $_FILES['gif_data']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error('文件上传失败');
}
$file = $_FILES['gif_data'];
// 验证文件类型
$allowed_types = array('image/gif');
$filetype = wp_check_filetype(basename($file['name']));
if (!in_array($filetype['type'], $allowed_types)) {
wp_send_json_error('仅支持GIF格式');
}
// 准备上传文件
$upload_overrides = array(
'test_form' => false,
'mimes' => array('gif' => 'image/gif')
);
// 上传文件
$upload = wp_handle_upload($file, $upload_overrides);
if (isset($upload['error'])) {
wp_send_json_error($upload['error']);
}
// 准备附件数据
$attachment = array(
'post_mime_type' => $filetype['type'],
'post_title' => sanitize_text_field($_POST['title'] ?? '动画作品'),
'post_content' => '',
'post_status' => 'inherit',
'post_author' => get_current_user_id()
);
// 插入附件到媒体库
$attachment_id = wp_insert_attachment($attachment, $upload['file']);
if (is_wp_error($attachment_id)) {
wp_send_json_error('插入媒体库失败');
}
// 生成附件元数据
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attachment_data = wp_generate_attachment_metadata($attachment_id, $upload['file']);
wp_update_attachment_metadata($attachment_id, $attachment_data);
// 返回成功响应
wp_send_json_success(array(
'attachment_id' => $attachment_id,
'url' => wp_get_attachment_url($attachment_id),
'edit_link' => admin_url('post.php?post=' . $attachment_id . '&action=edit')
));
}
// 注册AJAX脚本
add_action('wp_enqueue_scripts', 'register_animation_tools_ajax');
function register_animation_tools_ajax() {
if (is_page_template('animation-editor.php')) {
wp_localize_script('gif-generator', 'animationToolsAjax', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('animation_tools_nonce')
));
}
}
### 5.2 创建短代码功能
// 添加短代码,允许在文章/页面中插入动画工具
add_shortcode('animation_tool', 'animation_tool_shortcode');
function animation_tool_shortcode($atts) {
$atts = shortcode_atts(array(
'width' => '100%',
'height' => '600px',
'mode' => 'editor' // editor, preview, simple
), $atts);
ob_start();
if ($atts['mode'] === 'simple') {
// 简化版动画工具
?>
<div class="simple-animation-tool" style="width: <?php echo esc_attr($atts['width']); ?>; height: <?php echo esc_attr($atts['height']); ?>;">
<div class="simple-tool-header">
<h4>简易动画制作</h4>
</div>
<div class="simple-canvas-container">
<canvas class="simple-animation-canvas"></canvas>
</div>
<div class="simple-controls">
<button class="simple-draw-btn">绘制</button>
<button class="simple-clear-btn">清空</button>
<button class="simple-save-btn">保存为GIF</button>
</div>
</div>
<?php
} else {
// 完整版工具链接
$editor_url = home_url('/animation-editor/');
?>
<div class="animation-tool-link">
<h3>动画制作工具</h3>
<p>使用我们的内置工具创建自定义动画和GIF</p>
<a href="<?php echo esc_url($editor_url); ?>" class="button button-primary">
打开动画编辑器
</a>
</div>
<?php
}
return ob_get_clean();
}
## 第六部分:样式设计与优化
### 6.1 基础样式设计
/ animation-editor.css /
.animation-editor-container {
max-width: 1400px;
margin: 20px auto;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.editor-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
}
.editor-header h1 {
margin: 0 0 20px 0;
font-size: 28px;
}
.editor-tabs {
display: flex;
gap: 10px;
}
.tab-btn {
background: rgba(255,255,255,0.2);
border: none;
color: white;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
}
.tab-btn.active {
background: rgba(255,255,255,0.4);
}
.tab-btn:hover {
background: rgba(255,255,255,0.3);
}
.editor-main {
display: grid;
grid-template-columns: 250px 1fr 300px;
min-height: 600px;
}
.tool-panel {
background: #f8f9fa;
border-right: 1px solid #dee2e6;
padding: 20px;
}
.tool-section {
margin-bottom: 30px;
}
.tool-section h3 {
margin-top: 0;
color: #495057;
font-size: 16px;
border-bottom: 2px solid #667eea;
padding-bottom: 5px;
}
.tool-buttons {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px;
}
.tool-btn {
background: white;
border: 2px solid #dee2e6;
padding: 10px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.tool-btn.active {
border-color: #667eea;
background: #667eea;
color: white;
}
.tool-btn:hover {
border-color: #495057;
}
.property-controls {
display: flex;
flex-direction: column;
gap: 15px;
}
.control-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.control-group label {
font-size: 14px;
color: #6c757d;
}
.control-group input[type="range"] {
width: 100%;
}
.canvas-area {
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.canvas-container {
position: relative;
border: 2px dashed #dee2e6;
border-radius: 4px;
margin-bottom: 20px;
}
animation-canvas {
display: block;
background: white;
cursor: crosshair;
}
.canvas-overlay {
position: absolute;
top: 0;
left: 0;
right:
