
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@brain-toolkit/element-sizer
Advanced tools
一个强大的 DOM 元素展开/折叠动画工具,提供平滑的元素尺寸变化动画效果。
npm install @brain-toolkit/element-sizer
yarn add @brain-toolkit/element-sizer
pnpm add @brain-toolkit/element-sizer
import { ElementResizer } from '@brain-toolkit/element-sizer';
// 获取目标元素
const targetElement = document.getElementById('collapsible-content');
// 创建 ElementResizer 实例
const resizer = new ElementResizer({
target: targetElement,
placeholder: true, // 自动创建占位符
expandClassName: 'expanded',
collapseClassName: 'collapsed'
});
// 展开元素
resizer.expand();
// 折叠元素
resizer.collapse();
<div class="container">
<button onclick="toggleElement()">切换显示</button>
<div id="collapsible-content" class="collapsible-item">
<div class="content">
<h3>可折叠内容</h3>
<p>这里是需要展开/折叠的内容...</p>
<ul>
<li>列表项 1</li>
<li>列表项 2</li>
<li>列表项 3</li>
</ul>
</div>
</div>
</div>
.collapsible-item {
/* 必需:设置过渡动画 */
transition: all 300ms ease-in-out;
overflow: hidden;
background: #f5f5f5;
border-radius: 8px;
}
.collapsible-item.expanded {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: scale(1.02);
}
.collapsible-item.collapsed {
opacity: 0.7;
transform: scale(0.98);
}
.content {
padding: 20px;
}
new ElementResizer(options?: ElementResizerOptions)
interface ElementResizerOptions {
/** 目标元素 - 需要展开/折叠的元素 */
target?: HTMLElement;
/** 单位类型,默认为 "px" */
unit?: string;
/** 占位符配置
* - true: 自动创建占位符
* - false: 不使用占位符
* - HTMLElement: 使用指定的占位符元素
*/
placeholder?: boolean | HTMLElement;
/** 展开状态时添加的 CSS 类名 */
expandClassName?: string;
/** 折叠状态时添加的 CSS 类名 */
collapseClassName?: string;
/** 动画持续时间(毫秒),覆盖 CSS transition 设置 */
duration?: number;
/** 根容器元素,用于计算相对位置 */
root?: HTMLElement | Document | Window;
/** 自定义相对位置计算函数 */
getRelativeRect?: (
target: HTMLElement,
container?: HTMLElement | Document | Window
) => ElementResizerRect;
}
target: HTMLElement获取目标元素。
const targetElement = resizer.target;
animationState: AnimationState获取当前动画状态。
import { AnimationState } from '@brain-toolkit/element-sizer';
console.log(resizer.animationState);
// 可能的值: 'idle' | 'expanding' | 'expanded' | 'collapsing' | 'collapsed'
isAnimating: boolean检查是否正在执行动画。
if (!resizer.isAnimating) {
resizer.expand();
}
expand(): void展开目标元素。
resizer.expand();
collapse(): void折叠目标元素。
resizer.collapse();
fixedCurrentTargetRect(): void固定当前目标元素的位置和尺寸。
resizer.fixedCurrentTargetRect();
cancelAnimation(): void取消当前正在进行的动画。
resizer.cancelAnimation();
setOptions(options: Partial<ElementResizerOptions>): ElementResizer更新配置选项,支持链式调用。
resizer
.setOptions({ expandClassName: 'new-expanded' })
.setOptions({ duration: 500 });
getPlaceholder(): HTMLElement | null获取占位符元素。
const placeholder = resizer.getPlaceholder();
if (placeholder) {
placeholder.style.backgroundColor = 'red';
}
getRelativeRect(target: HTMLElement): ElementResizerRect获取目标元素的相对位置和尺寸。
const rect = resizer.getRelativeRect(targetElement);
console.log(rect); // { top, left, width, height, right, bottom }
toUnit(value: string | number): string将数值转换为带单位的字符串。
console.log(resizer.toUnit(100)); // "100px"
console.log(resizer.toUnit('50%')); // "50%"
destroy(): void销毁实例并清理资源。
resizer.destroy();
interface ElementResizerRect {
top: string | number;
right: string | number;
bottom: string | number;
left: string | number;
width: string | number;
height: string | number;
}
enum AnimationState {
IDLE = 'idle',
EXPANDING = 'expanding',
EXPANDED = 'expanded',
COLLAPSING = 'collapsing',
COLLAPSED = 'collapsed'
}
class AccordionItem {
private resizer: ElementResizer;
private isExpanded = false;
constructor(private element: HTMLElement) {
const content = element.querySelector('.accordion-content') as HTMLElement;
this.resizer = new ElementResizer({
target: content,
placeholder: true,
expandClassName: 'accordion-expanded',
collapseClassName: 'accordion-collapsed'
});
// 初始状态为折叠
this.resizer.collapse();
}
toggle() {
if (this.isExpanded) {
this.resizer.collapse();
} else {
this.resizer.expand();
}
this.isExpanded = !this.isExpanded;
}
}
class AnimatedModal {
private resizer: ElementResizer;
constructor(private modalElement: HTMLElement) {
this.resizer = new ElementResizer({
target: modalElement,
expandClassName: 'modal-open',
collapseClassName: 'modal-closed',
duration: 300
});
}
show() {
this.modalElement.style.display = 'block';
this.resizer.expand();
}
hide() {
this.resizer.collapse();
// 动画完成后隐藏元素
setTimeout(() => {
this.modalElement.style.display = 'none';
}, 300);
}
}
class CollapsibleSidebar {
private resizer: ElementResizer;
private isCollapsed = false;
constructor(private sidebar: HTMLElement) {
this.resizer = new ElementResizer({
target: sidebar,
expandClassName: 'sidebar-expanded',
collapseClassName: 'sidebar-collapsed',
placeholder: false // 侧边栏通常不需要占位符
});
}
toggle() {
if (this.isCollapsed) {
this.resizer.expand();
} else {
this.resizer.collapse();
}
this.isCollapsed = !this.isCollapsed;
}
}
class ExpandableCard {
private resizer: ElementResizer;
private detailsElement: HTMLElement;
constructor(private cardElement: HTMLElement) {
this.detailsElement = cardElement.querySelector(
'.card-details'
) as HTMLElement;
this.resizer = new ElementResizer({
target: this.detailsElement,
placeholder: true,
expandClassName: 'details-expanded',
root: cardElement // 相对于卡片容器计算位置
});
// 绑定点击事件
const toggleButton = cardElement.querySelector('.toggle-details');
toggleButton?.addEventListener('click', () => this.toggleDetails());
}
toggleDetails() {
if (
this.resizer.animationState === 'collapsed' ||
this.resizer.animationState === 'idle'
) {
this.resizer.expand();
} else if (this.resizer.animationState === 'expanded') {
this.resizer.collapse();
}
}
}
const resizer = new ElementResizer({
target: targetElement,
getRelativeRect: (target, container) => {
// 自定义位置计算逻辑
const targetRect = target.getBoundingClientRect();
const containerRect =
container instanceof HTMLElement
? container.getBoundingClientRect()
: { top: 0, left: 0 };
return {
top: targetRect.top - containerRect.top,
left: targetRect.left - containerRect.left,
width: targetRect.width,
height: targetRect.height,
right: targetRect.right - containerRect.left,
bottom: targetRect.bottom - containerRect.top
};
}
});
const resizer = new ElementResizer({ target: targetElement });
// 监听动画状态变化
const checkAnimationState = () => {
switch (resizer.animationState) {
case 'expanding':
console.log('正在展开...');
break;
case 'expanded':
console.log('展开完成');
break;
case 'collapsing':
console.log('正在折叠...');
break;
case 'collapsed':
console.log('折叠完成');
break;
}
};
// 定期检查状态(实际使用中可能需要更优雅的方式)
const interval = setInterval(checkAnimationState, 100);
const resizer = new ElementResizer()
.setOptions({ target: targetElement })
.setOptions({ expandClassName: 'expanded' })
.setOptions({ collapseClassName: 'collapsed' })
.setOptions({ duration: 400 });
// 延迟操作
setTimeout(() => resizer.expand(), 1000);
setTimeout(() => resizer.collapse(), 3000);
目标元素必须设置 CSS transition 属性:
.target-element {
/* 必需:指定需要动画的属性 */
transition:
width 300ms ease,
height 300ms ease,
top 300ms ease,
left 300ms ease;
/* 或者使用 all */
transition: all 300ms ease;
}
destroy() 方法// 检查动画状态
if (!resizer.isAnimating) {
resizer.expand();
}
// 组件销毁时清理
onDestroy(() => {
resizer.destroy();
});
动画不生效
位置计算错误
getRelativeRect 函数占位符问题
// 启用调试信息
const resizer = new ElementResizer({
target: targetElement,
placeholder: true
});
// 检查当前状态
console.log('Animation State:', resizer.animationState);
console.log('Is Animating:', resizer.isAnimating);
console.log('Target Rect:', resizer.getRelativeRect(targetElement));
console.log('Placeholder:', resizer.getPlaceholder());
ISC
欢迎提交 Issue 和 Pull Request!
查看 CHANGELOG.md 了解版本更新详情。
FAQs
一个强大的 DOM 元素展开/折叠动画工具,提供平滑的元素尺寸变化动画效果。
We found that @brain-toolkit/element-sizer demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.