You've already forked FrameTour-BE
feat(image): 添加图片叠加功能支持
- 新增 OverlayImageConfig 类用于配置叠加图片参数 - 支持通过 imageKey 从 dynamicData 动态获取图片 URL - 提供默认图片 URL 配置选项 - 支持设置叠加图片宽高比例(0.0-1.0 范围) - 实现图片适配模式配置(CONTAIN、COVER、FILL、SCALE_DOWN) - 添加圆角半径配置,支持自动圆形效果 - 支持水平垂直对齐方式设置(CENTER、LEFT、RIGHT、TOP、BOTTOM) - 提供水平垂直偏移量调节功能 - 更新配置验证逻辑,增加叠加图片配置校验 - 修改图片 URL 校验规则,支持动态数据填充 - 更新 JSON Schema 配置模板,包含叠加图片配置项
This commit is contained in:
@@ -29,9 +29,83 @@ public class ImageConfig implements ElementConfig {
|
||||
|
||||
/**
|
||||
* 圆角半径(像素,0为直角)
|
||||
* 注意:当 borderRadius >= min(width, height) / 2 时,效果为圆形
|
||||
*/
|
||||
private Integer borderRadius = 0;
|
||||
|
||||
/**
|
||||
* 叠加图片配置
|
||||
* 用于在主图上叠加另一张图片(如二维码中心的头像)
|
||||
*/
|
||||
private OverlayImageConfig overlayImage;
|
||||
|
||||
/**
|
||||
* 叠加图片配置类
|
||||
*/
|
||||
@Data
|
||||
public static class OverlayImageConfig {
|
||||
/**
|
||||
* 叠加图片的数据源 key(从 dynamicData 获取 URL)
|
||||
* 例如:faceAvatar
|
||||
*/
|
||||
private String imageKey;
|
||||
|
||||
/**
|
||||
* 叠加图片的默认 URL(当 dynamicData 中无对应值时使用)
|
||||
*/
|
||||
private String defaultImageUrl;
|
||||
|
||||
/**
|
||||
* 叠加图片宽度占主图宽度的比例(0.0 - 1.0)
|
||||
* 默认 0.45(与现有水印实现一致)
|
||||
*/
|
||||
private Double widthRatio = 0.45;
|
||||
|
||||
/**
|
||||
* 叠加图片高度占主图高度的比例(0.0 - 1.0)
|
||||
* 默认 0.45
|
||||
*/
|
||||
private Double heightRatio = 0.45;
|
||||
|
||||
/**
|
||||
* 叠加图片的适配模式
|
||||
* 默认 COVER(与现有水印实现一致)
|
||||
*/
|
||||
private String imageFitMode = "COVER";
|
||||
|
||||
/**
|
||||
* 叠加图片的圆角半径
|
||||
* 默认 -1 表示自动计算为圆形(min(width, height) / 2)
|
||||
*/
|
||||
private Integer borderRadius = -1;
|
||||
|
||||
/**
|
||||
* 叠加图片的水平对齐方式
|
||||
* CENTER - 居中(默认)
|
||||
* LEFT - 左对齐
|
||||
* RIGHT - 右对齐
|
||||
*/
|
||||
private String horizontalAlign = "CENTER";
|
||||
|
||||
/**
|
||||
* 叠加图片的垂直对齐方式
|
||||
* CENTER - 居中(默认)
|
||||
* TOP - 顶部对齐
|
||||
* BOTTOM - 底部对齐
|
||||
*/
|
||||
private String verticalAlign = "CENTER";
|
||||
|
||||
/**
|
||||
* 水平偏移量(像素),正值向右,负值向左
|
||||
*/
|
||||
private Integer offsetX = 0;
|
||||
|
||||
/**
|
||||
* 垂直偏移量(像素),正值向下,负值向上
|
||||
*/
|
||||
private Integer offsetY = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
// 校验圆角半径
|
||||
@@ -50,12 +124,55 @@ public class ImageConfig implements ElementConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// 校验图片URL
|
||||
if (StrUtil.isBlank(defaultImageUrl)) {
|
||||
throw new IllegalArgumentException("默认图片URL不能为空");
|
||||
// 校验图片URL(注意:现在可以通过 dynamicData 动态填充,所以允许为空)
|
||||
if (StrUtil.isNotBlank(defaultImageUrl)) {
|
||||
if (!defaultImageUrl.startsWith("http://") && !defaultImageUrl.startsWith("https://")) {
|
||||
throw new IllegalArgumentException("图片URL必须以http://或https://开头: " + defaultImageUrl);
|
||||
}
|
||||
}
|
||||
if (!defaultImageUrl.startsWith("http://") && !defaultImageUrl.startsWith("https://")) {
|
||||
throw new IllegalArgumentException("图片URL必须以http://或https://开头: " + defaultImageUrl);
|
||||
|
||||
// 校验叠加图片配置
|
||||
if (overlayImage != null) {
|
||||
validateOverlayImage(overlayImage);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateOverlayImage(OverlayImageConfig overlay) {
|
||||
// 校验比例范围
|
||||
if (overlay.getWidthRatio() != null && (overlay.getWidthRatio() <= 0 || overlay.getWidthRatio() > 1)) {
|
||||
throw new IllegalArgumentException("叠加图片宽度比例必须在 0-1 之间: " + overlay.getWidthRatio());
|
||||
}
|
||||
if (overlay.getHeightRatio() != null && (overlay.getHeightRatio() <= 0 || overlay.getHeightRatio() > 1)) {
|
||||
throw new IllegalArgumentException("叠加图片高度比例必须在 0-1 之间: " + overlay.getHeightRatio());
|
||||
}
|
||||
|
||||
// 校验对齐方式
|
||||
if (StrUtil.isNotBlank(overlay.getHorizontalAlign())) {
|
||||
String align = overlay.getHorizontalAlign().toUpperCase();
|
||||
if (!"CENTER".equals(align) && !"LEFT".equals(align) && !"RIGHT".equals(align)) {
|
||||
throw new IllegalArgumentException("水平对齐方式只能是CENTER、LEFT或RIGHT: " + overlay.getHorizontalAlign());
|
||||
}
|
||||
}
|
||||
if (StrUtil.isNotBlank(overlay.getVerticalAlign())) {
|
||||
String align = overlay.getVerticalAlign().toUpperCase();
|
||||
if (!"CENTER".equals(align) && !"TOP".equals(align) && !"BOTTOM".equals(align)) {
|
||||
throw new IllegalArgumentException("垂直对齐方式只能是CENTER、TOP或BOTTOM: " + overlay.getVerticalAlign());
|
||||
}
|
||||
}
|
||||
|
||||
// 校验叠加图片URL
|
||||
if (StrUtil.isNotBlank(overlay.getDefaultImageUrl())) {
|
||||
if (!overlay.getDefaultImageUrl().startsWith("http://") && !overlay.getDefaultImageUrl().startsWith("https://")) {
|
||||
throw new IllegalArgumentException("叠加图片URL必须以http://或https://开头: " + overlay.getDefaultImageUrl());
|
||||
}
|
||||
}
|
||||
|
||||
// 校验适配模式
|
||||
if (StrUtil.isNotBlank(overlay.getImageFitMode())) {
|
||||
String mode = overlay.getImageFitMode().toUpperCase();
|
||||
if (!"CONTAIN".equals(mode) && !"COVER".equals(mode) && !"FILL".equals(mode) && !"SCALE_DOWN".equals(mode)) {
|
||||
throw new IllegalArgumentException("叠加图片适配模式只能是CONTAIN、COVER、FILL或SCALE_DOWN: " + overlay.getImageFitMode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +181,19 @@ public class ImageConfig implements ElementConfig {
|
||||
return "{\n" +
|
||||
" \"defaultImageUrl\": \"https://example.com/image.jpg\",\n" +
|
||||
" \"imageFitMode\": \"CONTAIN|COVER|FILL|SCALE_DOWN\",\n" +
|
||||
" \"borderRadius\": 0\n" +
|
||||
" \"borderRadius\": 0,\n" +
|
||||
" \"overlayImage\": {\n" +
|
||||
" \"imageKey\": \"faceAvatar\",\n" +
|
||||
" \"defaultImageUrl\": \"https://example.com/default-avatar.png\",\n" +
|
||||
" \"widthRatio\": 0.45,\n" +
|
||||
" \"heightRatio\": 0.45,\n" +
|
||||
" \"imageFitMode\": \"COVER\",\n" +
|
||||
" \"borderRadius\": -1,\n" +
|
||||
" \"horizontalAlign\": \"CENTER\",\n" +
|
||||
" \"verticalAlign\": \"CENTER\",\n" +
|
||||
" \"offsetX\": 0,\n" +
|
||||
" \"offsetY\": 0\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user