diff --git a/README.md b/README.md index 0b364fb..f3be6d3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,3 @@ -## Metrics - -```js -200 -- w - Start of the week, given as a Unix timestamp. -- a - Number of additions -- d - Number of deletions -- c - Number of commits -``` - ## how use 1. pull request your repo @@ -25,15 +15,13 @@ ## Design -- [] 自动裁剪圆角 -- [x] 左右自动居中特性 - - 如果当前行没有溢出,则会被居中 - +- [] Automatic clipping and rounding of Avatar +- [x] Horizontal Auto Center ![auto-center.svg](docs/default/auto-center.svg) ### size -### category +### category(TODO) ```diff export default { @@ -63,11 +51,26 @@ export default { ### circle +- `circle`: default: circle + +![](docs/circle/circle-default.svg) + +- `isRadius:false`: you can disabled the feature + +```diff +export default { ++ 'vuejs-translations/docs-zh-cn': { ++ isRadius: false, ++ } +} +``` +![](docs/circle/no-circle.svg) + ### margin ### text display -- [] configure text color? (I'm not sure if I need this feature..) +- [] configure text color? (It't necessary?) ### config @@ -75,6 +78,7 @@ maybe you need filter some users. emm, just like as `vuejs-translations/docs-zh-cn`, it's a Chinese translation for Vue docs community, we does't need show non Chinese contributors in our repo, actually, `vuejs-translations/docs-zh-cn` upstream repo is `vuejs/docs`. +in `config.ts` add your repo configure. ```diff export default { @@ -82,4 +86,4 @@ export default { + ignore: [] // if you need ignore some users + } } -``` \ No newline at end of file +``` diff --git a/config.ts b/config.ts index 92c102e..9d1b5d7 100644 --- a/config.ts +++ b/config.ts @@ -21,10 +21,10 @@ const config: ConfigItem = // https://github.com/vuejs-translations/docs-zh-cn 'vuejs-translations/docs-zh-cn': { ignore: [], // if you need ignore some users - size: 80, + size: 64, height: 200, width: 800, - fontSize: 10, + fontSize: 20, isRadius: false, } } diff --git a/dev.md b/dev.md new file mode 100644 index 0000000..0b8bbad --- /dev/null +++ b/dev.md @@ -0,0 +1,8 @@ +## Metrics + +```js +- w - Start of the week, given as a Unix timestamp. +- a - Number of additions +- d - Number of deletions +- c - Number of commits +``` diff --git a/docs/circle/circle-default.svg b/docs/circle/circle-default.svg new file mode 100644 index 0000000..15fd923 --- /dev/null +++ b/docs/circle/circle-default.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + veaba + + + + + veaba2 + + + + + veaba3 + + + + + veaba4 + + + + + veaba5 + + + + + veaba6 + + + + + veaba7 + + + + + veaba8 + + + + + veaba9 + + + + + veaba10 + + + + \ No newline at end of file diff --git a/docs/circle/no-circle.svg b/docs/circle/no-circle.svg new file mode 100644 index 0000000..350ec45 --- /dev/null +++ b/docs/circle/no-circle.svg @@ -0,0 +1,54 @@ + + + + + veaba + + + + + veaba2 + + + + + veaba3 + + + + + veaba4 + + + + + veaba5 + + + + + veaba6 + + + + + veaba7 + + + + + veaba8 + + + + + veaba9 + + + + + veaba10 + + + + \ No newline at end of file diff --git a/public/circle.svg b/public/circle.svg new file mode 100644 index 0000000..26ad90e --- /dev/null +++ b/public/circle.svg @@ -0,0 +1,12 @@ + + + + + + + + + veaba5 + + + diff --git a/repos/veaba/contributors.svg b/repos/veaba/contributors.svg index 7863594..0690543 100644 --- a/repos/veaba/contributors.svg +++ b/repos/veaba/contributors.svg @@ -1,89 +1,98 @@ + - - - veaba - - - - - veaba2 - - - - - veaba3 - - - - - veaba4 - - - - - veaba5 - - - - - veaba6 - - - - - veaba7 - - - - - veaba8 - - - - - veaba9 - - - - - veaba10 - - - - - veaba11 - - - - - veaba12 - - - - - veaba13 - - - - - veaba14 - - - - - veaba15 - - - - - veaba16 - - - - - veaba17 - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + veaba + + + + + veaba2 + + + + + veaba3 + + + + + veaba4 + + + + + veaba6 + + + + + veaba5 + + + + + veaba8 + + + + + veaba7 + + + + + veaba9 + + + + + veaba10 + + + + \ No newline at end of file diff --git a/repos/vuejs-translations/docs-zh-cn.svg b/repos/vuejs-translations/docs-zh-cn.svg index e772af0..350ec45 100644 --- a/repos/vuejs-translations/docs-zh-cn.svg +++ b/repos/vuejs-translations/docs-zh-cn.svg @@ -1,89 +1,54 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + veaba + + + + + veaba2 + + + + + veaba3 + + + + + veaba4 + + + + + veaba5 + + + + + veaba6 + + + + + veaba7 + + + + + veaba8 + + + + + veaba9 + + + + + veaba10 + + + + \ No newline at end of file diff --git a/src/assets/circle-demo1.svg b/src/assets/circle-demo1.svg new file mode 100644 index 0000000..47c993b --- /dev/null +++ b/src/assets/circle-demo1.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/assets/circle.svg b/src/assets/circle.svg index e5e5e62..11b7e9e 100644 --- a/src/assets/circle.svg +++ b/src/assets/circle.svg @@ -1,10 +1,18 @@ - + - - - - + + + + + + - - + + + veaba2 + + + + veaba2 + \ No newline at end of file diff --git a/src/assets/circle1.svg b/src/assets/circle1.svg new file mode 100644 index 0000000..e0fcff7 --- /dev/null +++ b/src/assets/circle1.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/core/index.ts b/src/core/index.ts index 3cc725a..3ab5714 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -8,7 +8,7 @@ import { writeFile } from 'node:fs/promises'; import { chunk } from 'lodash'; -import { svgStart, svgEnd, asyncHandleUsersSVG } from './svg'; +import { svgStart, svgEnd, asyncHandleUsersSVG, asyncHandlerUserDefsSVG } from './svg'; import { listTen } from '../../tests/mock' import { UserConfig, UserItem } from './types'; import { getOwnerRepo } from './utils'; @@ -59,7 +59,7 @@ const generateUserListSVG = async (userList: UserItem[], config: UserConfig) => splitList = chunk(userList, oneRowMax) } - const userBlockData = await asyncHandleUsersSVG(splitList, { + const svgConfig = { baseSize, fontSize, oneRowMax, @@ -67,39 +67,41 @@ const generateUserListSVG = async (userList: UserItem[], config: UserConfig) => svgWidth, svgHeight, isRadius, - }) + } - return `${svgStart(svgWidth, svgHeight)} - ${userBlockData} - ${svgEnd()} - ` + // radius + if (config.isRadius === undefined || config.isRadius === true) { + return `${svgStart(svgWidth, svgHeight)} + + ${await asyncHandlerUserDefsSVG(splitList, svgConfig)} + + +${await asyncHandleUsersSVG(splitList, svgConfig)} +${svgEnd()} + ` + } else if (config.isRadius === false) { + const userBlockData = await asyncHandleUsersSVG(splitList, svgConfig) + return `${svgStart(svgWidth, svgHeight)} + ${userBlockData} +${svgEnd()} + ` + } + return '' } const saveSVG = async (ownerRepo: string) => { - const { owner, repo } = getOwnerRepo && getOwnerRepo(ownerRepo) || {} if (!owner || !repo) { console.error('Invalid repo address:', ownerRepo) return } - const options: UserConfig = config[ownerRepo] - - // TODO add test log - let name = ownerRepo === 'veaba/contributors' ? ownerRepo.padEnd(29, ' ') : ownerRepo - name += ' =>' - if (options.isRadius !== false) { - - console.log(name, 'need radius=>') - } else { - console.log(name, 'not need radius=>') - } - - + const userConfig: UserConfig = config[ownerRepo] try { const controller = new AbortController(); - const svgStr = await generateUserListSVG(listTen, options) + const svgStr = await generateUserListSVG(listTen.slice(0, 10), userConfig) + const filename = `./repos/${owner}/${repo}.svg` const promiseWrite = writeFile(filename, svgStr, { encoding: 'utf-8' }) controller.abort(); diff --git a/src/core/svg.ts b/src/core/svg.ts index 886f6c5..aa5a1e6 100644 --- a/src/core/svg.ts +++ b/src/core/svg.ts @@ -1,83 +1,123 @@ import { imagePathToBase64 } from "./image" -import { SvgConfig, UserItem } from "./types" -import { autoCenter, getByteLen, getImageX, getImageY, getTextX, getTextY } from "./utils" +import { ImageXYItem, SvgConfig, UserItem, XYItem } from "./types" +import { autoCenter, getImageX, getImageY, getTextX, getTextY } from "./utils" export const svgStart = (width: number, height: number) => { return `` } -export const svgEnd = () => { - return `` -} +export const svgEnd = () => `` + // TODO export const svgNoFound = () => { } - - /** * generate svg block string - * @param userItem - * @param {number} xIndex 行 - * @param {number} yIndex 列 -* @yIndex ??? -* + * @param {UserItem} userItem + * @param {number} xyItem + * @param {number} svgConfig */ -export const svgBlock = async (userItem: UserItem, xIndex: number, yIndex: number, childrenLen: number, svgConfig: SvgConfig): Promise => { - const { baseSize, fontSize } = svgConfig +export const svgBlockANode = async (userItem: UserItem, xyItem: XYItem, childrenLen: number, svgConfig: SvgConfig): Promise => { + + const { xIndex, yIndex } = xyItem // image const imageX = getImageX(yIndex, svgConfig) + autoCenter(childrenLen, svgConfig) - const imageY = getImageY(xIndex, svgConfig) // TODO - const imageHeight = baseSize - const imageWidth = baseSize + const imageY = getImageY(xIndex, svgConfig) - // text - const textX = getTextX(yIndex, svgConfig) + autoCenter(childrenLen, svgConfig); - const textY = getTextY(xIndex, svgConfig) + // if BASE_SIZE too small,or font-size less than 20 + // hidden svg > text node + let SVGText = ''; + if (svgConfig.baseSize >= 50 && svgConfig.fontSize >= 20) { + SVGText = renderTextNode(childrenLen, xyItem, userItem, svgConfig) + } - // console.log( - // ` (xIndex,yIndex) => ( ${xIndex}, ${yIndex} ) - // (getImageX,getImageY) => ( ${imageX}, ${imageY} ) - // (getTextX,getTextY) => ( ${textX}, ${textY} )` - // ) + const SVGData = ` + + ${await renderImageNode({ imageX, imageY }, userItem, svgConfig)} + ${SVGText} + +` + return SVGData - // console.log('\n') +} + +export const svgBlockImage = async (userItem: UserItem, xyItem: XYItem, childrenLen: number, svgConfig: SvgConfig): Promise => { + const { xIndex, yIndex } = xyItem + // image + const imageX = getImageX(yIndex, svgConfig) + autoCenter(childrenLen, svgConfig) + const imageY = getImageY(xIndex, svgConfig) + const imageXYItem = { imageX, imageY } + return await renderImageNode(imageXYItem, userItem, svgConfig) +} +// isRadius = false +export const svgBlockCircle = async (userItem: UserItem, xyItem: XYItem, childrenLen: number, svgConfig: SvgConfig): Promise => { + const { xIndex, yIndex } = xyItem + const imageX = getImageX(yIndex, svgConfig) + autoCenter(childrenLen, svgConfig) + const imageY = getImageY(xIndex, svgConfig) + + return ` + + + + ` +} + +// render svg image node +const renderImageNode = async (imageXYItem: ImageXYItem, userItem: UserItem, svgConfig: SvgConfig) => { + const { imageX, imageY } = imageXYItem + const imageHeight = svgConfig.baseSize + const imageWidth = svgConfig.baseSize const localImagePath = 'public/avatars/' + userItem.id + '.jpg' const base64Image = await imagePathToBase64(localImagePath) const imageBase64Data = 'data:image/PNG;base64,' + base64Image + const clipPath = svgConfig.isRadius ? `clip-path="url(#${userItem.author})"` : '' + return ` ` +} - // if BASE_SIZE too small,or font-size less than 20 - // hidden svg > text node - let SVGText = `${userItem.author}` - if (baseSize <= 50 || fontSize < 20) { - SVGText = '' - } +// render svg text node +const renderTextNode = (childrenLen: number, xyItem: XYItem, userItem: UserItem, svgConfig: SvgConfig) => { + const { xIndex, yIndex } = xyItem + const textX = getTextX(yIndex, svgConfig) + autoCenter(childrenLen, svgConfig); + const textY = getTextY(xIndex, svgConfig) + return `${userItem.author}` +} - const aSVGData = ` - - - ${SVGText} - - ` - return aSVGData +// rectangle avatar +export const asyncHandleUsersSVG = async (splitList: (UserItem | UserItem[])[], config: SvgConfig) => { + let userBlockData = '' + await Promise.all(splitList.map(async (item, xIndex) => { + if (Array.isArray(item)) { + await Promise.all(item.map(async (child, yIndex) => { + const childBlock = await svgBlockANode(child, { xIndex, yIndex }, item.length, config) + userBlockData += childBlock + }) + ) + } else { + const currentBlock = await svgBlockANode(item, { xIndex: 0, yIndex: xIndex }, splitList.length, config) + userBlockData += currentBlock + } + })) + return userBlockData } -export const asyncHandleUsersSVG = async (splitList: (UserItem | UserItem[])[], config: SvgConfig) => { +// circle avatar +export const asyncHandlerUserDefsSVG = async (splitList: (UserItem | UserItem[])[], config: SvgConfig) => { let userBlockData = '' await Promise.all(splitList.map(async (item, xIndex) => { if (Array.isArray(item)) { await Promise.all(item.map(async (child, yIndex) => { - const childBlock = await svgBlock(child, xIndex, yIndex, item.length, config) + const childBlock = await svgBlockCircle(child, { xIndex, yIndex }, item.length, config) userBlockData += childBlock }) ) } else { - const currentBlock = await svgBlock(item, 0, xIndex, splitList.length, config) // ? BUG? 文字和图片对不齐了 + const currentBlock = await svgBlockCircle(item, { xIndex: 0, yIndex: xIndex }, splitList.length, config) userBlockData += currentBlock } })) diff --git a/src/core/types.d.ts b/src/core/types.d.ts index 7e48427..947e1e1 100644 --- a/src/core/types.d.ts +++ b/src/core/types.d.ts @@ -30,4 +30,14 @@ export interface SvgConfig { svgHeight: number outSize: number isRadius: boolean +} + +export interface XYItem { + xIndex: number + yIndex: number +} + +export interface ImageXYItem { + imageX: number + imageY: number } \ No newline at end of file diff --git a/src/pages/DisplaySVG.vue b/src/pages/DisplaySVG.vue index f19cc4d..c3146c6 100644 --- a/src/pages/DisplaySVG.vue +++ b/src/pages/DisplaySVG.vue @@ -17,8 +17,9 @@ const circleSVGData = circleSVG

SVG vuejs-translations/docs-zh-cn demo

-

SVG border-radius

+

SVG border-radius 1

+