Java生成微信分享海报【基础设计】
Quaint 人气:0前言
微信后台生成海报一般都是一个模板写死,然后就完事了,过了不久让修改个模板,就又要看半天,还要考虑是否重新复制一份改一改,越来越多的重复代码,全在一个图片类里,然后就越来越乱。这两天用设计模式处理了一下,让以后修改模板,新增模板更舒服一点。有第三方好用的轻量级的实现,还请留言。感激!!
起步
- 了解IO
- 了解awt
- 装饰者设计模式
开始
- demo地址
喜欢直接看项目的可以直接 >> demo-common
- 目录结构
抽象层(abst目录)
- 海报抽象类
/**
* 海报抽象类
* @author quaint
* @date 21 February 2020
* @since master
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class AbstractPoster implements Poster {
/**
* 背景图
*/
protected BufferedImage backgroundImage;
/**
* logo
*/
protected BufferedImage logo;
/**
* 广告语
*/
protected String slogan;
/**
* 主图
*/
protected BufferedImage mainImage;
/**
* 二维码
*/
protected BufferedImage qrcode;
}
- 海报装饰抽象类
/**
* 海报装饰抽象类
* @author quaint
* @date 21 February 2020
* @since master
*/
@Data
@AllArgsConstructor
public abstract class AbstractPosterDecorator implements Poster {
protected Poster poster;
protected int positionX;
protected int positionY;
protected int width;
protected int height;
public AbstractPosterDecorator(Poster poster){
this.poster = poster;
}
@Override
public BufferedImage draw(BufferedImage image) {
System.out.println("默认绘制方法");
return poster.draw(image);
}
}
- 海报接口定义
/**
* 海报接口定义
* @author quaint
* @date 21 February 2020
* @since master
*/
public interface Poster {
/**
* 画海报
* @param image image
* @return image
*/
BufferedImage draw(BufferedImage image);
}
- 海报绘制接口定义
/**
* 海报绘制接口定义
* @author quaint
* @date 21 February 2020
* @since master
*/
public interface PosterDraw<T>{
/**
* 是否支持
* @param clazz class
* @return bool
*/
boolean support(Class<?> clazz);
/**
* 通过数据绘制 并返回 图片
* @param data 海报所需的数据
* @return 图片
* @throws IOException io
*/
BufferedImage drawAndReturnImage(T data) throws IOException;
}
具体海报特有属性(content目录)
/**
* 朋友小卡片海报
* @author quaint
* @date 21 February 2020
* @since master
*/
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class MiniAppCardPoster extends AbstractPoster {
/**
* 用户头像
*/
private BufferedImage headImage;
/**
* 用户昵称
*/
private String userNickName;
/**
* 价格范围
*/
private String priceRange;
/**
* 划线价
*/
private String linePrice;
@Builder(toBuilder = true)
public MiniAppCardPoster(BufferedImage backgroundImage, BufferedImage logo, String slogan, BufferedImage mainImage, BufferedImage qrcode, BufferedImage headImage, String userNickName, String priceRange, String linePrice) {
super(backgroundImage, logo, slogan, mainImage, qrcode);
this.headImage = headImage;
this.userNickName = userNickName;
this.linePrice = linePrice;
this.priceRange = priceRange;
}
@Override
public BufferedImage draw(BufferedImage image) {
return image;
}
}
装饰者实现代码(decorators目录)
- 背景装饰
/**
* 背景装饰
* @author quaint
* @date 21 February 2020
* @since master
*/
public class BackgroundDecorator extends AbstractPosterDecorator {
public BackgroundDecorator(Poster poster) {
super(poster);
}
@Builder(toBuilder = true)
public BackgroundDecorator(Poster poster, int positionX, int positionY, int width, int height) {
super(poster,positionX,positionY,width,height);
}
@Override
public BufferedImage draw(BufferedImage image) {
// 绘制 被装饰之前的 图片
BufferedImage draw = poster.draw(image);
// 装饰, 绘制头像
return drawBackground(draw);
}
/**
* 绘制背景具体实现
* @param image image
* @return image
*/
private BufferedImage drawBackground(BufferedImage image){
// 如果宽度没变化
if (width == image.getWidth() && height == image.getHeight()){
return image;
}
// 调整背景宽度
if (width!=0 && height !=0){
BufferedImage newImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics2D g = newImage.createGraphics();
g.drawImage(image,0,0,width,height,null);
g.dispose();
return newImage;
}
// 绘制背景
return image;
}
}
- 图片装饰
/**
* 绘制 图片
* @author quaint
* @date 21 February 2020
* @since master
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class ImageDecorator extends AbstractPosterDecorator {
/**
* 要绘制的图片
*/
private BufferedImage image;
/**
* 是否修改为圆形
*/
private boolean circle;
public ImageDecorator(Poster poster) {
super(poster);
}
@Builder(toBuilder = true)
public ImageDecorator(Poster poster, int positionX, int positionY, int width, int height, BufferedImage image, boolean circle) {
super(poster,positionX,positionY,width,height);
this.image = image;
this.circle = circle;
}
@Override
public BufferedImage draw(BufferedImage image) {
// 绘制 被装饰之前的 图片
BufferedImage draw = poster.draw(image);
// 装饰, 绘制头像
return drawImage(draw);
}
/**
* 绘制图片具体实现
* @param sourceImage sourceImage
* @return image
*/
private BufferedImage drawImage(BufferedImage sourceImage){
if (image == null){
return sourceImage;
}
// 实现绘制图片
Graphics2D g = sourceImage.createGraphics();
if (circle){
// 设置形状
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, image.getWidth(), image.getHeight());
BufferedImage output = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2 = output.createGraphics();
output = g2.getDeviceConfiguration().createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT);
g2 = output.createGraphics();
// 将背景设置为透明。如果注释该段代码,默认背景为白色.也可通过g2.setPaint(paint) 设置背景色
g2.setComposite(AlphaComposite.Clear);
g2.fill(new Rectangle(image.getWidth(), image.getHeight()));
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1));
g2.setClip(shape);
// 使用 setRenderingHint 设置抗锯齿
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(image, 0, 0, null);
g2.dispose();
image = output;
}
g.drawImage(image, positionX, positionY, width, height, null);
g.dispose();
return sourceImage;
}
}
- 文本装饰
/**
* 绘制文本
* @author quaint
* @date 21 February 2020
* @since master
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class TextDecorator extends AbstractPosterDecorator {
/**
* 字体
*/
private Font font = new Font(null);
/**
* 字体样式
*/
private int fontStyle = Font.PLAIN;
/**
* 字体大小
*/
private int fontSize = 16;
/**
* 字体颜色
*/
private Color color = new Color(255,255,255);
/**
* 内容
*/
private String content;
/**
* 是否包含删除先
*/
private boolean delLine = false;
public TextDecorator(Poster poster) {
super(poster);
}
@Builder(toBuilder = true)
public TextDecorator(Poster poster, int positionX, int positionY, int width, int height, Font font, int fontSize, Color color, String content, int fontStyle, boolean delLine) {
super(poster,positionX,positionY,width,height);
this.font = font;
this.fontSize = fontSize;
this.color = color;
this.content = content;
this.fontStyle = fontStyle;
this.delLine = delLine;
}
@Override
public BufferedImage draw(BufferedImage image) {
// 绘制 被装饰之前的 图片
BufferedImage draw = poster.draw(image);
// 装饰, 绘制文本
return drawText(draw);
}
/**
* 绘制文本具体实现
* @param image image
* @return image
*/
private BufferedImage drawText(BufferedImage image){
if (StringUtils.isEmpty(content)){
return image;
}
// 实现绘制文本
font = font.deriveFont(fontStyle, fontSize);
Graphics2D g = image.createGraphics();
// 设置文字抗锯齿算法
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING , RenderingHints.VALUE_ANTIALIAS_ON);
g.setFont(font);
g.setColor(color);
g.drawString(content, positionX, positionY+fontSize);
if (delLine){
// 计算非汉字长度
int shortNum = content.replaceAll("[^0-9,a-z,A-Z,.]", "").length();
// 汉字长度
int longNum = content.length()-shortNum;
// 删除线长度 = (汉字长度 * size) + ((字符长度+1) * size/2)
int num = longNum + (shortNum+1)/2;
g.drawLine(positionX-fontSize/3,positionY+3*fontSize/5,positionX+fontSize*num,positionY+3*fontSize/5);
}
g.dispose();
return image;
}
}
绘制具体类型海报(draw目录)
/**
* @author quaint
* @date 21 February 2020
* @since master
*/
@Component
@Slf4j
public class MiniAppCardDraw implements PosterDraw<MiniAppCardPoster> {
@Override
public boolean support(Class<?> clazz) {
if (clazz == null){
return false;
}
return clazz.equals(MiniAppCardPoster.class);
}
@Override
public BufferedImage drawAndReturnImage(MiniAppCardPoster poster) throws IOException{
log.info("[drawAndReturnImage] method start, param:[{}]", poster);
// ======= 主逻辑开始 --> =======
// 1. 绘制背景 最好取背景的 width 和 height
BackgroundDecorator drawBg = new BackgroundDecorator(poster).toBuilder()
.width(420).height(336).build();
// 2. 绘制头像
ImageDecorator drawHead = new ImageDecorator(drawBg).toBuilder()
.positionX(27).positionY(27)
.width(36).height(36)
.circle(true)
.image(poster.getHeadImage()).build();
// 3. 绘制昵称
TextDecorator drawNickName = new TextDecorator(drawHead).toBuilder()
.positionX(71).positionY(32)
.fontSize(18)
.content(poster.getUserNickName()+" 向你推荐").build();
// 3. 绘制商品介绍
TextDecorator drawSlogan = new TextDecorator(drawNickName).toBuilder()
.positionX(27).positionY(70)
.fontSize(22).fontStyle(Font.BOLD)
.content(poster.getSlogan()).build();
// 4. 绘制商品图片
ImageDecorator drawProdImg = new ImageDecorator(drawSlogan).toBuilder()
.positionX(24).positionY(129)
.width(168).height(168)
.image(poster.getMainImage()).build();
// 5. 绘制价格返回
TextDecorator drawPriceRange = new TextDecorator(drawProdImg).toBuilder()
.positionX(203).positionY(155)
.fontSize(24).fontStyle(Font.BOLD)
.color(new Color(216,11,42))
.content(poster.getPriceRange()).build();
// 6. 绘制删除线价格
TextDecorator drawLinePrice = new TextDecorator(drawPriceRange).toBuilder()
.positionX(240).positionY(187)
.fontSize(18).delLine(true)
.color(new Color(153,153,153))
.content(poster.getLinePrice()).build();
// 调用最后一个包装类的 draw 方法
BufferedImage drawResult = drawLinePrice.draw(poster.getBackgroundImage());
// ======= <-- 主逻辑结束 =======
log.info("[drawAndReturnImage] method end, result:[success]");
return drawResult;
}
}
效果图
加载全部内容