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 🙏

© 2024 – Pkg Stats / Ryan Hefner

img-mark

v0.0.72

Published

在一个图片中标记一个或者多个截图区域

Downloads

11

Readme

Img-Mark

在一个图片中标记一个或者多个截图区域

在截图区域中标记一个或者多个子区域

如下图:

使用

引入样式

//main.ts
import 'img-mark/dist/style.css'

示例

<template>
	<div class="page-test-img-mark">
		<div style="width: 50vw; height: 100vh; background: #ccc; box-sizing: border-box; flex-shrink: 0">
			<ImgMark
				ref="imgMarkRef"
				:src="src"
				v-model:mode="mode"
				v-model:tagList="tagList"
				v-model:cropList="cropList"
				:enableDrawCropOutOfImg="false"
				:enableDrawTagOutOfCrop="false"
				:enableDrawTagOutOfImg="false"
			></ImgMark>
		</div>
		<div class="info-panel">
			<el-input v-model="src"></el-input>
			<el-button type="primary" size="small" style="margin-top: 40px" @click="getGroupInfo()">getGroupInfo</el-button>
			<el-alert
				v-for="item in tagList"
				@close="removeTag([item])"
				@mouseenter="setHoverItem(item)"
				@mouseleave="removeHoverItem(item)"
				style="margin-top: 20px"
				:key="uid(6)"
				:title="JSON.stringify(item)"
				type="warning"
			>
			</el-alert>
		</div>
	</div>
</template>
<script setup lang="ts">
import { ImgMark, Mode, BoundingBox } from 'img-mark'
import { uid } from 'uid'
let src = $ref('https://forza.ismcdn.jp/mwimgs/8/e/1774n/img_8e8307dc5355e41385fd3568ef95f233218536.jpg')
let mode = $ref<Mode>('crop')
let cropList = $ref<BoundingBox[]>([
	{
		startX: 0,
		startY: 0,
		endX: 1774,
		endY: 100,
	},
	{
		startX: 200,
		startY: 200,
		endX: 1000,
		endY: 500,
	},
])

let tagList = $ref<BoundingBox[]>([
	{
		startX: 50,
		startY: 0,
		endX: 100,
		endY: 50,
		isShow: true,
		labelText: 'customTagLabelText',
	},
	{
		startX: 0,
		startY: 0,
		endX: 1774,
		endY: 100,
		isShow: true,
	},
])

let imgMarkRef = $ref<InstanceType<typeof ImgMark>>()

function removeTag(data?: BoundingBox[]) {
	if (data) {
		imgMarkRef.removeTagItems(data)
	}
}
function cropChange() {
	removeTag()
}

function setHoverItem(item: BoundingBox) {
	item.showOutLine = true
}
function removeHoverItem(item: BoundingBox) {
	item.showOutLine = false
}

function getGroupInfo() {
	let groupInfo = imgMarkRef.getTagListGroupByCropIndex()
	console.log(groupInfo)
}
</script>
<style scoped lang="scss">
.page-test-img-mark {
	display: flex;
	justify-content: space-between;
	.info-panel {
		width: 50vw;
		box-sizing: border-box;
		padding: 20px;
	}
}
</style>

类型

type ShortCutItem = 'ctrl+b' | 'space'

type Event = {
	onClick?: (e: unknown, item: BoundingBox) => void //tag click事件
	onDoubleClick?: (e: unknown, item: BoundingBox) => void //tag double click事件
}

type WH = {
	width: number
	height: number
}

type Hooks = {
	shiftMode(): void //切换模式
	shiftDrawSwitch(onOrOff: 'on' | 'off'): void //切换是否开始画的开关
	init(): void //初始化组件
	resize(): void //resize后重新初始化组件
}

type BoundingBox = {
	startX: number
	endX: number
	startY: number
	endY: number
	isShow?: boolean //tag是否显示
	showOutLine?: boolean //tag是否边框是否高亮
	labelText?: string //tag的标签文字
	tagConfig?: TagConfig //单独定义某个tagConfig
	cropConfig?: CropConfig //单独定义某个cropConfig
} & Event

type Dot = {
	x: number
	y: number
	raduis: number
	__scale?: number
	__index?: number
	__isHover?: boolean
	dotConfig?: DotConfig
} & Event

type CropConfig = {
	lineDash?: number[]
	strokeStyle?: string
	lineWidth?: number
	customDraw?: CustomDraw
}

export type DaubConfig = {
	strokeStyle?: string
	lineWidth?: number
}

type LayerConfig = {
	fillStyle?: string
}

export type CustomDraw = (
	ctx: CanvasRenderingContext2D,
	info: {
		target: BoundingBox | undefined
		positions: Rect
	}
) => void

type TagConfig = {
	fontSize?: number //px单位,默认20
	showText?: boolean //是否展示tag index
	fillStyle?: string
	textFillStyle?: string
	hoverStrokeStyle?: string
	hoverLineWidth?: number
	hoverLineDash?: number[]
	highlightStrokeStyle?: string
	highlightLineWidth?: number
	highlightLineDash?: number[]
	customDraw?: CustomDraw
}

type Point = {
	x: number
	y: number
}

type DaubPoint = Point & {
	lineWidth?: number
	strokeStyle?: string
}

type DotConfig = {
	lineDash?: number[]
	strokeStyle?: string
	fillStyle?: string
	hoverFillStyle?: string
	lineWidth?: number
	radius?: number
}

type ResizeEmitType = {
	index: number
	box: BoundingBox
}

type TagListGroupByCropIndex = {
	[key: number]: BoundingBox[]
	undefined: BoundingBox[]
}

type MouseOverInfoEmitType = {
	canvas: Point | null
	img: Point | null
}

type OnLoadImageEmitType = {
	status: 'loading' | 'success' | 'error'
	msg?: string
}

type TagListChangeType = 'add' | 'delete' | 'statusChange'

type TagListChangeEmitRetunType = {
	type: TagListChangeType
	list: BoundingBox[]
	parentCrop?: BoundingBox
}

type CropListChangeType = 'add' | 'delete' | 'resize'

type CropListChangeEmitType = {
	type: CropListChangeType
	list: BoundingBox[]
}

type BoundingBox2Rect = (boundingBoxList: BoundingBox[]) => Rect[]
type CustomDrawTopCtx = (ctx: CanvasRenderingContext2D, boundingBox2Rect: BoundingBox2Rect) => void

组件属性

| 属性 | 说明 | 类型 | 可选值 | 默认值 | | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ----------------- | ------ | | src | 图片地址, 必选参数 | string | —— | —— | | mode/v-model:mode | 模式 | string | crop/tag/daub/dot | crop | | mobileOperation/v-model:mobileOperation | 移动端单指在画布滑动是移动还是画 rect | string | move/draw | move | | cropList/v-model:cropList | 裁切区域集合,相对于 img左上角开始定位 | BoundingBox[] | —— | [] | | tagList/v-model:tagList | tag 区域集合,相对于 img左上角开始定位 | BoundingBox[] | —— | [] | | dotList/v-model:dotList | dot 集合,相对于 img左上角开始定位 | Dot[] | —— | [] | | daubStack/v-model:daubStack | 涂抹区域店集合,相对于 img左上角开始定位 | DaubPoint[][] | —— | [] | | precision | BoundingBox 精度 | number | —— | 0 | | drawingText | 画 tag时候展示的文字 | number | —— | 0 | | initScale | 是否自动缩放图片适应到画布大小 | boolean | true/false | true | | enableScale | 是否允许缩放画布 | boolean | true/false | true | | enableMove | 是否允许移动画布 | boolean | true/false | true | | enableDrawCrop | 是否允许在画布上画 crop | boolean | true/false | true | | enableDrawTag | 是否允许在画布上画 tag | boolean | true/false | true | | enableInteractiveTagChangeStatus | 是否允许交互改变 tag 状态 | boolean | true/false | true | | enableInteractiveCropDelete | 是否允许交互删除 crop | boolean | true/false | true | | enableDrawCropOutOfImg | 是否允许 crop 画到图片外 | boolean | true/false | true | | enableDrawTagOutOfCrop | 是否允许 tag 画到 crop 外 | boolean | true/false | true | | enableDrawTagOutOfImg | 是否允许 tag 画到图片外 | boolean | true/false | true | | enableDotCenterOutOfImg | 是否允许 dot 画到图片外 | boolean | true/false | false | | splitClickAndDoubleClickEvent | 是否分离单击和双击事件 | boolean | true/false | false | | disableDefaultShortcuts | 禁用默认快捷键 | ShortCutItem[] | —— | [] | | enableCropResize | 是否允许 crop 改变大小 | boolean | true/false | true | | enableTagResize | 是否允许 tag 改变大小 | boolean | true/false | false | | enableCropCross | 是否允许 crop 和其他 crop 相交,不允许后,如果相交,新画的 crop会不添加,如果是 resize 操作相交以后,按照下方 handleResizeCropCross 属性去处理 | boolean | true/false | true | | handleResizeCropCross | 当 enableCropCross属性为 false,resize 操作相交后该如何处理进行 resize 操作的 crop | string | delete/reset | reset | | isShowTip | 是否显示底部提示区域 | boolean | true/false | false | | isCropSingle | 是否单个 crop , 在添加 crop 的时候自动删除旧的 crop | boolean | true/false | false | | isImgCrop | 是否在 cropList 为空时默认以图片大小为裁切区域 | boolean | true/false | false | | layerConfig | 浮层样式 | LayerConfig | —— | —— | | cropConfig | crop 样式 | CropConfig | —— | —— | | tagConfig | tag 样式 | TagConfig | —— | —— | | daubConfig | daub 样式 | DaubConfig | —— | —— | | dotConfig | dot 样式 | DotConfig | —— | —— | | customDrawTopCtx | 自定义绘制 | CustomDrawTopCtx | —— | —— |

组件事件

| 事件名 | 说明 | 参数 | | -------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------- | | tagListChange | 当添加或者删除或者改变组件状态 tag 项触发,parentCrop 只在 typeadd 时候返回 | data:TagListChangeEmitRetunType | | cropListChange | 当添加或者删除或者改变 crop 大小触发 | {type:'add'/'delete'/'resize', list:BoundingBox[] } | | resizeStart | crop 开始 resize 触发 | data:ResizeEmitType | | resizeEnd | crop 结束 resize 触发 | data:ResizeEmitType | | delCrop | 删除 crop 触发 | list:BoundingBox[] | | drawCropStart | 添加 crop 之前触发 | —— | | drawTagStart | 添加 tag 之前触发 | —— | | mouseOverInfo | 鼠标在组件上移动或者移除时候触发 | info:MouseOverInfoEmitType | | onLoadImage | 图片加载状态事件 | data:OnLoadImageEmitType |

组件暴露对象

| 对象 | 类型 | 说明 | | --------- | ------------------------------------------------ | ---------------------------------------------------------------- | | hooks | Hooks | 调用组件抽象方法 | | scaleInfo | {scale:numberzoomScale:number} | scale 为初始缩放到容器时候的缩放值zoomScale 为继续缩放的值 | | info | {imgWH:WH} | 一些常见的信息 |

组件暴露方法

| 方法 | 说明 | 参数 | 返回类型 | | -------------------------- | ---------------------------------------------- | ----------------------------------------- | ----------------------- | | removeTagItems | 移除 tag 项 | list:BoundingBox[] | void | | render | 重新渲染 | —— | void | | getTagListGroupByCropIndex | 获取 tagList 并按照 cropIndex 分组 | type: 'startPoint'/'allIn' = 'startPoint' | TagListGroupByCropIndex | | getBase64ImageData | 获取原图大小绘制完各种元素后的 base64 图片 url | crossOrigin?: boolean | Promise<string> | | scrollIntoView | 讲某个区域滚动到 0,0 点 | box: BoundingBox | void |

Slots

| 插槽名 | 说明 | | ------ | -------------------- | | tip | 右下角提示自定义内容 |

Lib 方法

| 方法 | 说明 | 参数 | 返回类型 | | ----------------------------- | ------------------------------ | ------------------------------------------------------------------- | --------------- | | transformTagListBoxRelativeTo | 转换 tag list 基于什么去定位 | type: 'img'/'crop', cropInfo: BoundingBox, tagList: BoundingBox[] | BoundingBox[] | | transformTagBoxRelativeTo | 转换单个 tag 基于什么去定位 | type: 'img'/'crop', cropInfo: BoundingBox, tag: BoundingBox | BoundingBox | | boxIsAllInOtherBox | box 是否完全包含在另一个 box | box: BoundingBox, otherBox: BoundingBox | boolean |

操作

移动画布

按住鼠标左键拖动

画区域

按下空格 + 按住鼠标左键拖动

切换模式

Ctrl + B

放大缩小

鼠标滚轮