Web 最佳实践
性能优化
1. 选择合适的处理模式
视频模式(ProcessMode.Video)
- 适合实时视频流、直播场景
- 性能更优,处理速度快
- 建议用于相机预览、视频通话等场景
图像模式(ProcessMode.Image)
- 适合单张图片处理
- 质量更高,效果更好
- 建议用于照片编辑、图片美化等场景
javascript
// 实时视频处理
const result = engine.processImage(
imageData,
imageData.width,
imageData.height,
ProcessMode.Video
);
// 高质量图片处理
const result = engine.processImage(
imageData,
imageData.width,
imageData.height,
ProcessMode.Image
);2. 参数调节建议
美颜参数调节原则
- 从较小值开始调节,避免过度美颜
- 实时场景下建议降低参数值以保证流畅度
- 静态图片可以适当提高参数值
- 根据用户群体和场景调整参数范围
推荐参数范围
javascript
// 基础美颜参数(实时场景)
engine.setBasicParam(BasicParam.Smoothing, 0.2); // 磨皮 (0.0-1.0)
engine.setBasicParam(BasicParam.Whitening, 0.1); // 美白 (0.0-1.0)
engine.setBasicParam(BasicParam.Rosiness, 0.1); // 红润 (0.0-1.0)
// 面部重塑参数(实时场景)
engine.setReshapeParam(ReshapeParam.FaceThin, 0.1); // 瘦脸 (0.0-1.0)
engine.setReshapeParam(ReshapeParam.EyeSize, 0.1); // 大眼 (0.0-1.0)
// 静态图片可以适当提高参数值
engine.setBasicParam(BasicParam.Smoothing, 0.5);
engine.setBasicParam(BasicParam.Whitening, 0.3);3. 内存优化
复用 Canvas 和 ImageData 对象
javascript
class ImageProcessor {
constructor() {
this.canvas = null;
this.ctx = null;
this.imageData = null;
}
initCanvas(width, height) {
if (!this.canvas) {
this.canvas = document.createElement('canvas');
this.canvas.width = width;
this.canvas.height = height;
this.ctx = this.canvas.getContext('2d');
} else {
// 如果尺寸变化,更新 Canvas
if (this.canvas.width !== width || this.canvas.height !== height) {
this.canvas.width = width;
this.canvas.height = height;
}
}
}
async processFrame(videoElement, engine) {
this.initCanvas(videoElement.videoWidth, videoElement.videoHeight);
// 绘制视频帧到 Canvas
this.ctx.drawImage(videoElement, 0, 0);
// 获取图像数据(复用 ImageData)
this.imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
// 处理图像
const result = engine.processImage(
this.imageData,
this.canvas.width,
this.canvas.height,
ProcessMode.Video
);
// 绘制结果
this.ctx.putImageData(result, 0, 0);
return this.canvas;
}
}及时释放资源
javascript
// 页面卸载时释放资源
window.addEventListener('beforeunload', () => {
if (engine) {
engine.destroy();
engine = null;
}
});
// Vue 组件
onUnmounted(() => {
if (engine) {
engine.destroy();
engine = null;
}
});
// React 组件
useEffect(() => {
return () => {
if (engine) {
engine.destroy();
engine = null;
}
};
}, []);4. 使用 Web Worker(可选)
对于大量图像处理,可以考虑使用 Web Worker 避免阻塞主线程:
javascript
// worker.js
import { BeautyEffectEngine, EngineConfig } from 'facebetter';
let engine = null;
self.onmessage = async function(e) {
const { type, data } = e.data;
switch (type) {
case 'init':
try {
const config = new EngineConfig(data.config);
engine = new BeautyEffectEngine(config);
await engine.init();
self.postMessage({ type: 'init', success: true });
} catch (error) {
self.postMessage({ type: 'init', success: false, error: error.message });
}
break;
case 'process':
try {
const result = engine.processImage(
data.imageData,
data.width,
data.height,
data.processMode || ProcessMode.Video
);
self.postMessage({
type: 'process',
success: true,
result: result
});
} catch (error) {
self.postMessage({
type: 'process',
success: false,
error: error.message
});
}
break;
}
};架构设计
1. 单例模式管理引擎
javascript
class BeautyEngineManager {
static instance = null;
static engine = null;
static async getInstance(config) {
if (!this.instance) {
this.instance = new BeautyEngineManager();
await this.instance.init(config);
}
return this.instance;
}
async init(config) {
if (!this.engine) {
const engineConfig = new EngineConfig(config);
this.engine = new BeautyEffectEngine(engineConfig);
await this.engine.init();
}
}
getEngine() {
return this.engine;
}
destroy() {
if (this.engine) {
this.engine.destroy();
this.engine = null;
}
this.instance = null;
}
}
// 使用
const manager = await BeautyEngineManager.getInstance({
appId: 'your-app-id',
appKey: 'your-app-key'
});
const engine = manager.getEngine();2. 参数管理类
javascript
class BeautyParamManager {
constructor(engine) {
this.engine = engine;
this.params = {
basic: {
smoothing: 0,
whitening: 0,
rosiness: 0
},
reshape: {
faceThin: 0,
eyeSize: 0
},
makeup: {
lipstick: 0,
blush: 0
}
};
}
setBasicParam(type, value) {
this.params.basic[type] = value;
this.engine.setBasicParam(BasicParam[type], value);
}
setReshapeParam(type, value) {
this.params.reshape[type] = value;
this.engine.setReshapeParam(ReshapeParam[type], value);
}
setMakeupParam(type, value) {
this.params.makeup[type] = value;
this.engine.setMakeupParam(MakeupParam[type], value);
}
reset() {
Object.keys(this.params.basic).forEach(key => {
this.setBasicParam(key, 0);
});
Object.keys(this.params.reshape).forEach(key => {
this.setReshapeParam(key, 0);
});
Object.keys(this.params.makeup).forEach(key => {
this.setMakeupParam(key, 0);
});
}
getParams() {
return JSON.parse(JSON.stringify(this.params));
}
}3. 图像处理管道
javascript
class ImageProcessingPipeline {
constructor(engine) {
this.engine = engine;
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
}
processImage(source, options = {}) {
try {
// 1. 准备 Canvas
this.canvas.width = source.width || source.videoWidth;
this.canvas.height = source.height || source.videoHeight;
// 2. 绘制源图像
this.ctx.drawImage(source, 0, 0);
// 3. 获取图像数据
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
// 4. 处理图像(同步方法,无需 await)
const result = this.engine.processImage(
imageData,
this.canvas.width,
this.canvas.height,
options.processMode || ProcessMode.Video
);
// 5. 绘制结果
this.ctx.putImageData(result, 0, 0);
return this.canvas;
} catch (error) {
console.error('图像处理失败:', error);
throw error;
}
}
}用户体验优化
1. 加载状态提示
javascript
async function initEngineWithLoading(config) {
showLoading('正在初始化美颜引擎...');
try {
const engine = new BeautyEffectEngine(config);
await engine.init();
hideLoading();
showSuccess('美颜引擎初始化成功');
return engine;
} catch (error) {
hideLoading();
showError('美颜引擎初始化失败: ' + error.message);
throw error;
}
}2. 渐进式加载
javascript
async function initEngineProgressive(config) {
// 1. 显示加载提示
updateLoadingStatus('正在加载 WASM 模块...');
const engine = new BeautyEffectEngine(config);
// 2. 初始化(内部会加载 WASM)
updateLoadingStatus('正在初始化引擎...');
await engine.init();
// 3. 启用美颜类型
updateLoadingStatus('正在配置美颜参数...');
engine.setBeautyTypeEnabled(BeautyType.Basic, true);
updateLoadingStatus('完成');
return engine;
}3. 错误重试机制
javascript
async function initEngineWithRetry(config, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const engine = new BeautyEffectEngine(config);
await engine.init();
return engine;
} catch (error) {
if (i < maxRetries - 1) {
console.warn(`初始化失败,${1000 * (i + 1)}ms 后重试...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
} else {
throw error;
}
}
}
}安全建议
1. 保护 AppID 和 AppKey
不要在前端代码中硬编码密钥:
javascript
// ❌ 不推荐:硬编码在代码中
const config = new EngineConfig({
appId: 'your-app-id',
appKey: 'your-app-key'
});
// ✅ 推荐:从服务器获取或使用环境变量
async function getConfig() {
const response = await fetch('/api/config');
const { appId, appKey } = await response.json();
return new EngineConfig({ appId, appKey });
}2. 使用 HTTPS
确保在生产环境中使用 HTTPS,保护数据传输安全。
3. 验证用户输入
javascript
function validateConfig(config) {
if (!config.appId || typeof config.appId !== 'string') {
throw new Error('appId 必须是字符串');
}
if (!config.appKey || typeof config.appKey !== 'string') {
throw new Error('appKey 必须是字符串');
}
return true;
}