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

c2m-demo

v0.2.13

Published

Customize-2D

Readme

ToolsInfo

{
    areaIndex: 0,
    area: {x: 168, y: 205, w: 170, h: 226},
    transformId: "490fbb84-c7a2-4d8b-9f97-4b73e6445f46",
    resourceId: 'xxx',
    canvasSize: {w: 512, h: 512},
    isActive: true,
    isText: false,
    text: {
        content: 'test',
        font: 'Microsoft Yahei',
        size: 30,
        color: '#ff0000'
    }
}

Initialize

options

const options = {
    size: {
        w: 1024,
        h: 1024
    },
    customs: [{
        area: {
            x: 10,
            y: 10,
            w: 100,
            h: 100,
            outline: {
                show: true,
                color: {
                    normal: '#000000',
                    active: '#cc0000'
                }
            }
        },
        limitArea: {
            left: 100,
            top: 100,
            right: 100,
            bottom: 100
        },
        mask: {
            url: getImageUrl('/images/swatch-full-mask-1024x1024.png'),
            alpha: {
                inside: 1.0,
                outside: 0.5
            }
        },
        outline: {
            show: true,
            color: '#666666'
        }
    }],
    buttons: {
            transform: '...',
            delete: '...',
            edit: '...'
    },
    onLayerActivityStateChanged: (info) => {},
    onTextEditable: (info) => {}
};

options.layerAutoActive: boolean (new)

If set to true, when the layer is added to the custom area (or text layer updated), it will be active default: true

options.customs[].outline: object

.show: value is false does not show the outline .color: color for the border of active layer

options.customs[].area.outline: object

.color: color for the border of custom area

options.customs[].limitArea: object

left/top/right/bottom: pixel value, which represents the distance between the limit area and the canvas

options.customs[].mask: object

mask for layers in current custom area

import {
    Customize,
    CUSTOM_LAYER_FLIP_TYPE_HORIZONTAL,
    CUSTOM_LAYER_FLIP_TYPE_VERTICAL,
    CUSTOM_LAYER_MOVE_DIRECTION_UP,
    CUSTOM_LAYER_MOVE_DIRECTION_DOWN
} from 'customize2d';

//initialize
const c2d = new Customize({
    //...
    //layer checked state
    onLayerActivityStateChanged: (info) => {
        const elemTool = document.querySelector('#tools');
        if(info.isActive) {
            toolsInfo = info;
            const container = document.querySelector('#canvasContainer');
            const x = (container.clientWidth - info.canvasSize.w) / 2 + info.area.x + info.area.w / 2;
            const y = info.area.y - elemTool.clientHeight - 5;
            elemTool.style.left = `${x}px`;
            elemTool.style.top = `${y}px`;
            elemTool.setAttribute('class', 'show');
        } else {
            toolsInfo = info;
            elemTool.removeAttribute('class');
        }
    }
});

Callbacks

On text edit button clicked

/**
 * @param {object} info
 * @param {number} info.areaIndex
 * @param {string} info.positionId
 */
const callbackOnTextEdit = (info) => {};
options.onTextEdit = callbackOnTextEdit;

API

Layer flip horizontal

c2d.layerFlip(toolsInfo, CUSTOM_LAYER_FLIP_TYPE_HORIZONTAL);

Layer flip vertial

c2d.layerFlip(toolsInfo, CUSTOM_LAYER_FLIP_TYPE_VERTICAL);

Layer move up

c2d.layerMove(toolsInfo, CUSTOM_LAYER_MOVE_DIRECTION_UP);

Layer move down

c2d.layerMove(toolsInfo, CUSTOM_LAYER_MOVE_DIRECTION_DOWN);

Layer replace

const url = '';
const id = '';
c2d.layerReplace(toolsInfo, url, id, (info) => {
    console.log('Replaced:', info);
});

Add text layer with svg

Support emoji customization. The path path of emojis is processed in the svgData parameter of SVGText.

import { SVGText } from 'customize2d';

const info = c2d.getCustomAreaInfo();

const color = '#f65401';
const size = '30'; // 开启自适应后是否限制字体最大字号,若不限制则为null
const content = svgData.map(cont => {
    const webIconDtos = cont.webIconDtos.map(dto => {
        if(dto.tag === 'emoji') {
            dto.tagSVG = emojiList[dto.unicode].path;
        }
        return dto;
    });
    cont.webIconDtos = webIconDtos;
    return cont;
});
const autoSize = true; // 是否开启文字自适应
// 自适应区域大小设置
const areaSize = {
    w,
    h
}
const svgText = new SVGText(content, color, size,  autoResize, areaSize);
svgText.init();

c2d.addText(info, svgText, (obj) => {
    //...
});

Update text layer with svg

import { SVGText } from 'customize2d';

const color = '#f65401';
const size = '30';
const svgText = new SVGText(svgData, color, size);
svgText.init();
c2d.updateText(toolsInfo, svgText, (obj) => {
    //...
});

Layer delete

c2d.layerDelete(toolsInfo, (info) => {
    //...
});

Set preview mode

//enable
c2d.setPreview(true);

//disable
c2d.setPreview(false);

Unactive layer

c2d.layerUnpick(() => {
    //
});

Get information of current custom area

const info = c2d.getCustomAreaInfo();

Check if the image layer can be deleted directly, even if it's not actived

the function will return true when there is only one image layer in current custom area

const info = c2d.getCustomAreaInfo();
const result = c2d.getUnpickedLayerReplaceable(info);
if(result.replaceable) {
    info.transformId = result.transformId;
    c2d.layerReplace(info, url, id, (ret) => {
        console.log('replaced: ', ret)
    });
} else {
    //
}

Active a custom area by positionId

const info = {positionId: 'position-0'};
c2d.areaPick(info);

Get a screenshot of a custom area square

const info = {areaIndex: 0};
c2d.getCustomizedSquareImage(info, (imgCodeBase64) => {
    const image = new Image();
    image.src = imgCodeBase64;
    container.appendChild(image);
});

Active image layer by positionId and resourceId

c2d.layerImagePick({positionId: info.positionId, resourceId}, (info) => {
    console.log(info);
});

Active text layer by positionId

c2d.layerTextPick({positionId: info.positionId}, (info) => {
    console.log(info);
});

Contribute

npm i
npm run dev

esample page

http://localhost:5859

Change Logs

1 针对竖排文字(一)优化了字体的高度显示(0.1.16)
2 调整了竖排文字之间的间距(0.1.16)
3 修复竖排文字多列展示问题(0.1.20)
4 调整了最大渲染底图尺寸至1600(0.1.23)
5 修复字体默认角度和特殊字符展示问题(0.1.24)
6 增加其他特殊字符渲染优化,中文下的,。【(等(0.1.24)
7 修复点击图层操作按钮时选中了下方图层(0.1.28)
8 0.2.0版本支持功能列表
    1)横排竖排文字扩展支持了Emoji表情定制;
    2)横竖排文字自适应定制区域大小;
    3)支持开启自适应后设置文字的最大字号;
9 修复竖排文字初始化渲染位置不正确(0.2.1)
10 优化表情文字混排排版、横排空格宽度、修复竖排空格过多时高度计算问题(0.2.2)
11 修复导出定制文字时忽略空格问题(0.2.3)
12 更新定制文字时保持原有的图层顺序(0.2.4)
13 修复表情文字混排竖排高度计算不准确(0.2.5)
14 针对不是正方形的表情进行兼容处理(0.2.6)
15 修复表情显示不全(0.2.7)
16 修复竖排平均高度计算问题(0.2.8)