首页 / 应用软件 / 详细教程,为网站打造内嵌的在线简易动画制作与GIF生成工具

详细教程,为网站打造内嵌的在线简易动画制作与GIF生成工具

详细教程:为WordPress网站打造内嵌在线简易动画制作与GIF生成工具

引言:为什么网站需要内置动画制作工具?

在当今数字时代,视觉内容已成为网站吸引用户、提升参与度的关键因素。动画和GIF图像因其生动、直观的表达方式,在社交媒体、产品演示、教程说明等场景中发挥着不可替代的作用。然而,对于大多数网站运营者来说,创建这些视觉元素通常需要依赖外部工具或专业设计人员,这不仅增加了成本,也降低了内容创作的灵活性。

通过为WordPress网站开发一个内置的简易动画制作与GIF生成工具,您可以:

  1. 大幅降低视觉内容创作门槛
  2. 提高内容生产效率
  3. 保持品牌视觉一致性
  4. 增强用户互动体验
  5. 减少对外部服务的依赖

本教程将详细指导您如何通过WordPress代码二次开发,实现这一实用功能,让您的网站具备专业级的简易动画制作能力。

第一部分:准备工作与环境配置

1.1 开发环境搭建

在开始开发之前,确保您已具备以下条件:

  1. 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境
  2. 代码编辑器:Visual Studio Code、Sublime Text或PHPStorm
  3. WordPress版本:5.0或更高版本
  4. 基础技能: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">&times;</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:
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5351.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部