【问题标题】:Selectively apply a CSS effect on a group of SVG elements有选择地对一组 SVG 元素应用 CSS 效果
【发布时间】:2021-08-26 13:38:04
【问题描述】:

我绘制了一组 3 个 svg 元素:一个蓝色正方形、一个红色圆圈和一个绿色椭圆(参见下面的代码)。 当鼠标悬停其中一个元素时,这三个元素会按比例放大。

我正在尝试应用稍微复杂一点的行为,但到目前为止没有成功:

。当鼠标悬停在蓝色方块上时,所有 3 个元素都应该按比例放大(这部分有效),

。当鼠标悬停在红色圆圈上时,只有红色圆圈和蓝色方块会放大(绿色椭圆不应该移动),

。当鼠标悬停在绿色椭圆上时,只有绿色椭圆和蓝色方块应该放大(红色圆圈不应该移动)。

是否有可能实现这种行为?

.zoom * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom:hover * {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}
<svg width="500" height="500">
  <g id="all_shapes" class="zoom">
    <rect id="square" x="20" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle" cx="170" cy="45" r="25" fill="red" />
    <ellipse id="ellipse" cx="170" cy="100" rx="40" ry="25" fill="green" />
  </g>
</svg>

编辑

仅使用 CSS 不是强制性的。如果更简单的话,也可以使用 JavaScript,也许可以按照以下步骤操作?

(1)从组'all_shapes'中获取所有元素'id,

(2) 默认创建一个id为'square'的数组,

(3) 追加悬停元素的id,

(4) 将 CSS '.zoom' 应用于数组元素。

【问题讨论】:

  • #2 和 #3 不能仅通过 CSS 实现,因为 CSS 选择器是级联的,它们只能选择 after 的元素,而不是 before 他们。
  • @Terry 谢谢。所以我假设我需要一些 JavaScript?像 (1) 列出特定组中名为“all_shapes”的所有元素的 ID,(2) 默认选择“方形”,以及 (3) 添加被鼠标悬停的元素的 ID?这样的任务似乎太复杂了,不是吗?
  • 我有一个想法,我回家后会测试它...
  • @biberman 通用兄弟选择器~ 只选择下游兄弟,从不选择上游。
  • @Terry: 我知道...

标签: javascript css svg


【解决方案1】:

这只能通过 CSS 实现。

.zoom * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom:hover *:not(#circle),
.zoom #square:hover ~ *,
.zoom #square:hover, 
.zoom #circle:hover {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}

.zoom #circle:hover ~ * {
  transform: scale(1);
  transform-origin: 50% 50%;
}
<svg width="500" height="500">
  <g id="all_shapes" class="zoom child">
    <rect id="square"  x="20" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle" cx="170"  cy="45" r="25" fill="red" />
    <ellipse id="ellipse" cx="170"  cy="100" rx="40" ry="25" fill="green" />
  </g>
</svg>

【讨论】:

  • 谢谢!您认为使用 JavaScript 函数更方便(如果存在多个组)可以实现吗?
  • @6PO0222 是的,使用 javascript/jquery 你可以更好地控制 dom,所以你当然可以做到。
  • 这很棒 - 使用纯 CSS! @6PO0222 这应该是公认的答案...
【解决方案2】:

由于 Sanjay Choudhary 发布了一个 imo 完美的纯 CSS 答案,这里有一个 Javascript 方法来做到这一点。


纯 Javascript

您只需使用 Javascript 切换一个类(此处为 .zoom),您的 CSS 几乎保持不变:

#all_shapes * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}

您首先将组#all_shapes 中的所有元素都存储在一个var 中(此处为elems)。然后你需要两个事件监听器,一个用于mouseover,一个用于mouseleave。

在mouseover 上,如果square 悬停(if (event.target.id == 'square')),则将.zoom 类添加到存储在elems 中的所有元素。否则,您只将该类添加到事件目标和正方形。在 mouseleave 上,您从存储在 elems 中的所有元素中删除类 .zoom。

工作示例:

let elems = document.querySelectorAll('#all_shapes *[id]');

document.querySelector('#all_shapes').addEventListener('mouseover', function(event) {
  if (event.target.id == 'square') {
    for (i = 0; i < elems.length; i++) {
      elems[i].classList.add('zoom');
    }
  } else if (!!event.target.id) {
    document.querySelector('#square').classList.add('zoom');
    event.target.classList.add('zoom');
  }
});

document.querySelector('#all_shapes').addEventListener('mouseleave', function() {
  for (i = 0; i < elems.length; i++) {
    elems[i].classList.remove('zoom');
  }
});
#all_shapes * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}
<svg width="500" height="500">
  <g id="all_shapes">
    <rect id="square"  x="20" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle" cx="170"  cy="45" r="25" fill="red" />
    <ellipse id="ellipse" cx="170"  cy="100" rx="40" ry="25" fill="green" />
  </g>
</svg>

jQuery

如果您使用 jQuery,代码会变得简单得多。你只需要一个事件监听器hover(),并且可以省略对var elems的绕道,因为它只需要两次,$('#all_shapes *[id]')很短,所以你可以直接使用它。

工作示例:

$('#all_shapes *[id]').hover(function() {
  if ($(this).is('#square')) {
    $('#all_shapes *[id]').toggleClass('zoom');
  } else {
    $('#square').toggleClass('zoom');
    $(this).toggleClass('zoom');
  }
});
#all_shapes * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<svg width="500" height="500">
  <g id="all_shapes">
    <rect id="square"  x="20" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle" cx="170"  cy="45" r="25" fill="red" />
    <ellipse id="ellipse" cx="170"  cy="100" rx="40" ry="25" fill="green" />
  </g>
</svg>

多个组

这里参考cmets是另一个例子,多组具有相同的元素和每个组独立的悬停效果。

不同之处在于您按类(此处为.all_shapes)选择组,并通过悬停目标的父级上的一个小弯道选择元素:

$(this).parent().find('...

要选择组的正方形,您可以使用 .square 类或 [id^="square"] 之类的属性选择器(这意味着 ID 以“正方形”开头)。

工作示例:

$('.all_shapes *[id]').hover(function() {
  if ($(this).is('[id^="square"]')) {
    $(this).parent().find('*[id]').toggleClass('zoom');
  } else {
    $(this).parent().find('[id^="square"]').toggleClass('zoom');
    $(this).toggleClass('zoom');
  }
});
.all_shapes * {
  transition: all .4s;
  transform-box: fill-box;
}

.zoom {
  transform: scale(1.2);
  transform-origin: 50% 50%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<svg width="500" height="500">
  <g id="all_shapes1" class="all_shapes">
    <rect id="square1"  x="20" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle1" cx="170"  cy="45" r="25" fill="red" />
    <ellipse id="ellipse1" cx="170"  cy="100" rx="40" ry="25" fill="green" />
  </g>
  <g id="all_shapes2" class="all_shapes">
    <rect id="square2"  x="220" y="20" width="100" height="100" fill="blue"/>
    <circle id="circle2" cx="370"  cy="45" r="25" fill="red" />
    <ellipse id="ellipse2" cx="370"  cy="100" rx="40" ry="25" fill="green" />
  </g>
</svg>

【讨论】:

  • jQuery 代码很棒!我试图通过使用一个类而不是'#all_shapes'和一个匹配'square'id的正则表达式来使其更通用。这是为了适应我有几个组的情况(例如,第 1 组 id="all_shapes_1' 和第 2 组 id="all_shapes_2",两者都具有相同的类;'square' 可以是 'square_1' 和 'square_2'分别)。知道怎么做吗?
  • 我不知道我是否理解正确:您的意思是一个具有多个组的 SVG,它们具有所有相同的元素 rect、circle 和 ellipse(具有不同的 ID)?并且悬停效果应该只适用于悬停组?
  • 是的。也许我应该在新线程中发布这个问题?
  • 不,我认为这只是一个小改动。我会在测试后更新我的答案...
  • 太棒了,太棒了!
猜你喜欢
  • 2020-04-12
  • 2010-09-08
  • 1970-01-01
  • 2022-07-11
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多