npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@chnak/poster

v1.0.20

Published

新一代海报制作插件 - 基于 @chnak/paper,支持多层级、组件化、相对坐标

Readme

FKPoster

npm version License: MIT

基于 Paper.js 的新一代海报制作插件,支持多层级、组件化、相对坐标定位。

GitHub: https://github.com/chnak/FKPoster

特性

  • 多层级管理 - 支持创建多个图层 (Layer),方便管理复杂海报的不同区域
  • 组件化开发 - 通过 createComponent 创建可复用组件,支持背景色和子元素组合
  • 相对坐标 - 支持百分比定位 ('50%'),轻松实现居中、适配不同尺寸
  • 丰富的组件库 - 内置 30+ 常用组件:Button、Badge、Card、Avatar、Progress、Rating、Icon、Quote 等
  • 文本自动换行 - Card、Quote 等组件支持中文文本自动换行
  • 多种导出格式 - 支持导出 PNG、SVG、Buffer、Base64

安装

npm install @chnak/poster

本地开发:

npm install

快速开始

const { PosterBuilder, TextElement, Button, Badge } = require('@chnak/poster')

async function main() {
  // 1. 创建海报构建器
  const poster = new PosterBuilder({
    width: 1080,
    height: 1920,
    backgroundColor: '#ffffff'
  })

  // 2. 创建图层
  const layer = poster.createLayer({ name: 'main', zIndex: 0 })

  // 3. 添加元素
  layer.addElement(new TextElement({
    x: 100, y: 200,
    text: 'Hello World',
    fontSize: 48,
    color: '#000000'
  }))

  layer.addElement(new Badge({
    x: 100, y: 300,
    text: 'NEW',
    fontSize: 18,
    backgroundColor: '#3b82f6',
    color: '#ffffff'
  }))

  // 4. 导出
  await poster.exportPNG('my-poster', './output')
  poster.destroy()
}

main().catch(console.error)

核心概念

PosterBuilder - 海报构建器

主入口类,负责创建画布、管理图层、导出图片。

const poster = new PosterBuilder({
  width: 1080,        // 海报宽度
  height: 1920,       // 海报高度
  backgroundColor: '#ffffff'  // 背景色
})

// 使用预设尺寸
poster.usePreset('poster_square')   // 1080x1080
poster.usePreset('poster_9_16')     // 1080x1920

// 创建图层
const layer = poster.createLayer({ name: 'main', zIndex: 0 })

// 导出
await poster.exportPNG('filename', './output')

Layer - 图层

图层类,类似轨道,管理一组元素,支持 zIndex 排序。

const layer = poster.createLayer({
  name: 'main',
  zIndex: 0
})

layer.addElement(element)

Component - 组件

可复用的元素组合,支持背景色和子元素管理。

// 创建组件 (已自动添加到 poster 管理)
const card = poster.createComponent({
  x: 100,
  y: 100,
  width: 300,
  height: 200,
  backgroundColor: '#3b82f6'
})

// 添加子元素 (坐标相对于组件)
card.addElement(new TextElement({
  x: 20, y: 20,
  text: '标题',
  fontSize: 24,
  color: '#ffffff'
}))

基础元素

| 元素 | 说明 | |------|------| | TextElement | 文本元素 | | RectElement | 矩形元素 | | CircleElement | 圆形元素 | | ImageElement | 图片元素 | | DividerElement | 分隔线 |

高级组件

| 组件 | 说明 | |------|------| | Button | 按钮 | | Badge | 徽章/标签 | | Card | 卡片 | | CTA | 行动号召按钮 | | Chip | 小标签 | | Avatar | 头像 | | Divider | 分隔线 | | Progress | 进度条 | | Rating | 星级评分 | | Quote | 引用块 | | Timeline | 时间线 | | Star | 星星 | | Feature | 特性展示 | | FeatureGrid | 特性网格 | | StatCard | 统计卡片 | | ListItem | 列表项 | | ProgressCircle | 圆形进度 | | Notification | 通知 | | ImageFrame | 图片框架 | | Arrow | 箭头 | | Bubble | 气泡 | | Ribbon | 缎带 | | Seal | 印章 | | Watermark | 水印 | | Icon | 图标 | | TagCloud | 标签云 | | Stepper | 步骤指示器 | | Table | 表格 | | HighlightText | 高亮文本 | | Grid | 网格 | | Columns | 多列布局 | | Barcode | 条形码 | | QRCode | 二维码 | | Frame | 装饰边框 | | Chart | 图表 |

完整示例

名片

const { PosterBuilder, Avatar, TextElement, Divider, Badge } = require('@chnak/poster')

async function main() {
  const poster = new PosterBuilder({
    width: 600,
    height: 400,
    backgroundColor: '#f8fafc'
  })

  const layer = poster.createLayer({ name: 'main', zIndex: 0 })

  // 头像
  layer.addElement(new Avatar({
    x: 80, y: 80,
    size: 80,
    initials: 'LN',
    backgroundColor: '#6366f1'
  }))

  // 姓名
  layer.addElement(new TextElement({
    x: 180, y: 90,
    text: '李明',
    fontSize: 28,
    fontWeight: 'bold',
    color: '#1e293b'
  }))

  // 职位
  layer.addElement(new TextElement({
    x: 180, y: 130,
    text: '高级产品经理',
    fontSize: 16,
    color: '#64748b'
  }))

  // 分隔线
  layer.addElement(new Divider({
    x: 80, y: 180,
    width: 440,
    thickness: 1,
    color: '#e2e8f0'
  }))

  // 联系信息
  layer.addElement(new TextElement({
    x: 80, y: 210,
    text: '邮箱:[email protected]',
    fontSize: 14,
    color: '#475569'
  }))

  layer.addElement(new TextElement({
    x: 80, y: 240,
    text: '电话:138-8888-8888',
    fontSize: 14,
    color: '#475569'
  }))

  // 标签
  layer.addElement(new Badge({
    x: 80, y: 300,
    text: '活跃用户',
    fontSize: 12,
    backgroundColor: '#dbeafe',
    color: '#3b82f6',
    radius: 4
  }))

  await poster.exportPNG('namecard', './output')
  poster.destroy()
}

main().catch(console.error)

活动海报

const { PosterBuilder, TextElement, Button, Badge, Card, Divider } = require('@chnak/poster')

async function main() {
  const poster = new PosterBuilder({
    width: 1080,
    height: 1920,
    backgroundColor: '#0f172a'
  })

  const layer = poster.createLayer({ name: 'main', zIndex: 0 })

  // 标题
  layer.addElement(new TextElement({
    x: '50%', y: 200,
    text: '春季大促',
    fontSize: 72,
    fontWeight: 'bold',
    color: '#ffffff',
    textAlign: 'center',
    anchor: [0.5, 0]
  }))

  // 副标题
  layer.addElement(new TextElement({
    x: '50%', y: 300,
    text: '全场低至 5 折',
    fontSize: 36,
    color: '#fbbf24',
    textAlign: 'center',
    anchor: [0.5, 0]
  }))

  // 按钮
  layer.addElement(new Button({
    x: '50%', y: 500,
    width: 300,
    height: 80,
    text: '立即抢购',
    fontSize: 28,
    backgroundColor: '#ef4444',
    color: '#ffffff',
    radius: 12,
    anchor: [0.5, 0]
  }))

  await poster.exportPNG('poster-event', './output')
  poster.destroy()
}

main().catch(console.error)

运行示例

# 基本示例
npm run example:basic

# 组件示例
npm run example:component

# 布局示例
npm run example:layout

# 或直接运行
node examples/namecard-demo.js
node examples/poster-event.js
node examples/poster-movie.js

API 文档

详细参数说明请参阅 skill.md

项目结构

src/
├── core/           # 核心类
│   ├── PosterBuilder.js  # 海报构建器
│   ├── Layer.js          # 图层
│   ├── Component.js       # 组件基类
│   └── BaseElement.js     # 元素基类
├── elements/        # 基础元素
│   ├── TextElement.js
│   ├── RectElement.js
│   ├── CircleElement.js
│   ├── ImageElement.js
│   └── DividerElement.js
├── components/      # 高级组件
│   ├── Button.js
│   ├── Badge.js
│   ├── Card.js
│   └── ...
└── index.js        # 入口文件

注意事项

  1. 坐标系统 - 所有元素的 x, y 都是相对于父容器的左上角
  2. 百分比支持 - 位置和尺寸支持百分比字符串,如 '50%'
  3. 组件管理 - createComponent 创建的组件已自动由 poster 管理,不需要再添加到 layer
  4. 销毁清理 - 使用完 poster 后调用 destroy() 方法释放资源
  5. 字体回退 - 中文文本会自动使用字体回退链确保渲染效果

License

MIT