从 SVG 三角形剪辑平滑过渡到圆形

问题描述 投票:0回答:2

我正在使用 SVG 将图像剪切到三角形。我的目标是让路径扩展并在悬停时变成一个圆圈(平滑过渡)。

我发现的最接近的是这个 codepen,作为对站点挑战的回应: http://codepen.io/enjikaka/pen/hCGjE 它确实会过渡,但它似乎与我正在做的剪辑有很大不同。我对 svg 很陌生(才刚刚开始使用它来创建这些三角形)。

这是我的代码(引导程序 col-sm-4 div 中的向上三角形剪辑和向下三角形剪辑):

<div class="col-sm-4">
              <div class='tri-up'>
                            <svg width="100%" height="100%" viewBox="0 0 100 87">
                              <clipPath id="clipTriangleUp">
                                <polygon points="0 87,100 87,50 0"/>
                              </clipPath>
                              <image clip-path="url(#clipTriangleUp)" preserveAspectRatio="none" width="100%" height="100%" xlink:href="http://placehold.it/560x484"/>
                            </svg>
                        </div>
                    </a>
                </div>
                <div class="col-sm-4">
                                           <div class='tri-down'>
                            <svg width="100%" height="100%" viewBox="0 0 100 87">
                              <clipPath id="clipTriangleDown">
                                <polygon points="0 0,100 0,50 87"/>
                              </clipPath>
                              <image clip-path="url(#clipTriangleDown)" preserveAspectRatio="none" width="100%" height="100%" xlink:href="http://placehold.it/560x484"/>
                            </svg>
                        </div>
                    </a>
                </div>

我这里还有其他一些类,它可以制作 6 个三角形(上下),分成两行,每行三个。负边距将三角形推得比引导列靠得更近。

如果有人知道如何实现这一点,我已经在互联网上搜索无果,非常感谢您的帮助。

html css svg hover transition
2个回答
0
投票

没有库和 JS 就没有简单的解决方案。然而,我们可以通过一些妥协来做到这一点。 我们将讨论的解决方案/部分:

  1. SVG 动画:了解发生了什么
  2. 完美的圆形到近乎完美的三角形
  3. 有点圆到完美的三角形
  4. 将圆固定为完美三角形

SVG 动画:了解发生了什么

让我们首先了解一下我们在做什么。为此,我们将使用 SVG。 我们使用带有 arc 命令的路径来制作一个完美的圆。然后我们增加这三个圆弧的半径。这将导致接近平坦的曲线。最后,我们添加一个真正的三角形并缩小假三角形。最后一步可以省略。我用点标记了角,并将真正的三角形涂成灰度,以便更容易看到正在发生的事情。

// tested with symmetrical triangle
// height width
h=w= 200
// center of circle, radius
cx=cy=r= w/2
// width of the right/left half triangle
w_tri=w_tri_ri=w_tri_le=75
// length of the triangle from the center to bottom
h_tri_bo=h_tri_bo_ri=h_tri_bo_le=Math.sqrt(r**2-w_tri**2)//=Math.sqrt(r**2-w_tri_ri**2)=Math.sqrt(r**2-w_tri_le**2)

// untranslated coordinates
// console.log(cx,0) //100 0
// console.log(cx+w_tri_ri,cy+h_tri_bo_ri) //175 166.14378277661478
// console.log(cx-w_tri_ri,cy+h_tri_bo_le) //25 166.14378277661478

// translated to be at the centre (centre of the circle translated to the origin)
console.log(0,cy) //0 100
console.log(w_tri_ri,h_tri_bo_ri) //75 66.14378277661477
console.log(-w_tri_ri,h_tri_bo_le) //-75 66.14378277661477
<svg width="200" height="200">
    <circle cx="0" cy="-100" r="2" fill="blue" transform="translate(100 100)"/>
    <circle cx="75" cy="66.14378277661477" r="2" fill="blue" transform="translate(100 100)"/>
    <circle cx="-75" cy="66.14378277661477" r="2" fill="blue" transform="translate(100 100)"/>
    <path transform="translate(100 100)" 
        d="M0,-100
            L 75 66.14378277661477
            -75 66.14378277661477
            0 -100" fill="grey"
    />
    <path transform="translate(100 100)">
        <animate 
            id="morph-ani"
            attributeName="d" dur="2s" fill="freeze"
            calcMode="spline"
            keyTimes="0;1"
            keySplines ="1 0 .85 1"
            from="M0,-100
                A100,100 0 0,1 75 66.14378277661477
                A100,100 0 0,1 -75 66.14378277661477
                A100,100 0 0,1 0 -100"
            to="M0 -100
                A1000,1000 0 0,1 75 66.14378277661477
                A1000,1000 0 0,1 -75 66.14378277661477
                A1000,1000 0 0,1 0 -100"
        />
        <animateTransform 
            additive="sum" 
            attributeName="transform"
            type="scale" 
            dur="2s"
            fill="freeze"
            from="1"
            to=".9"
            begin="1.6s"
        />
    />
</svg>

完美的圆形到近乎完美的三角形

现在我们只需要将其转换为 CSS 过渡,例如通过悬停来驱动它。我们将末端半径增加 10。这将使曲线更加线性,并且我们不必处理第二个三角形。 我们还在圆形/三角形周围添加一个 div。这确保了即使形状在光标下滑走,它仍然算作悬停状态。

专业人士:
  • 非常接近三角形
缺点:
  • 调整宽松是相当困难的。即使制作线性动画也很困难。
  • 取消悬停动画会导致动画过于突然。
  • 尖锐的结局

示例

#hoverhandler, #shape{
    height: 200px;
    width: 200px;
}

#hoverhandler #shape{
    transition: background-color 0s, clip-path 2s;
    transition-timing-function: cubic-bezier(.3,1,0.2,1);
    background-color: black;
    clip-path: path("M100,0 \
        A100,100 0 0,1 175 166.14378277661478 \
        A100,100 0 0,1 25 166.14378277661478 \
        A100,100 0 0,1 100 0Z")
}
#hoverhandler:hover #shape{
    clip-path: path( "M100,0 \
        A10000,10000 0 0,1 175 166.14378277661478 \
        A10000,10000 0 0,1 25 166.14378277661478 \
        A10000,10000 0 0,1 100 0Z");
    transition: background-color 0s, clip-path 2s;
    transition-timing-function: cubic-bezier( 0.8, 0, 0.7, 0);
}
<div id="hoverhandler">
    <div id="shape"></div>
</div>

如何计算坐标

// tested with symmetrical triangle
// height width
h=w= 200
// center of circle, radius
cx=cy=r= w/2
// width of the right/left half triangle
w_tri=w_tri_ri=w_tri_le=75
// length of the triangle from the center to bottom
h_tri_bo=h_tri_bo_ri=h_tri_bo_le=Math.sqrt(r**2-w_tri**2)//=Math.sqrt(r**2-w_tri_ri**2)=Math.sqrt(r**2-w_tri_le**2)

// untranslated coordinates
// console.log(cx,0)
// console.log(cx+w_tri_ri,cy+h_tri_bo_ri)
// console.log(cx-w_tri_le,cy+h_tri_bo_le)

// translated to be at the centre (centre of the circle translated to the origin)
console.log(0,-cy)
console.log(w_tri_ri,h_tri_bo)
console.log(-w_tri_le,h_tri_bo)

有点圆到完美的三角形

如果不调整缓动函数,之前示例的动画会变得非常快。为了缓解这个问题,我们可以创建一条完美三角形的路径,并尝试将其变形为尽可能接近圆形。

专业人士:
  • 时机没有那么爆炸性
  • 无法快速动画
缺点:
  • 圆并不是真正的圆

示例

#hoverhandler, #shape{
    height: 200px;
    width: 200px;
}
#hoverhandler #shape{
    transition: background-color 0s, clip-path 2s .5s;
    background-color: black;
    clip-path: path("M100,0 \
        C 203,0 219.97777228809804,126.4771161099481 175,166.14378277661478  \
        C 130.02222771190196,205.81044944328144 69.97777228809804,205.81044944328144 25,166.14378277661478  \
        C -19.977772288098038,126.4771161099481 -3,0 100,0Z")
}
#hoverhandler:hover #shape{
    transition: background-color 0s, clip-path 2s .4s;
    clip-path: path( "M100,0 \
        C 100,0 175,166.14378277661478 175,166.14378277661478 \
        C 175,166.14378277661478 25,166.14378277661478 25,166.14378277661478 \
        C 25,166.14378277661478 100,0 100,0Z");
}
<div id="hoverhandler">
    <div id="shape"></div>
</div>

如何计算坐标

(见下一节)

将圆固定为完美三角形

这个“圆”看起来不像一个圆。所以我们可以使用 Paul LeBau 的解决方案来修复明显的缺陷。

专业人士:
  • 时机没有那么爆炸性
  • 无法加快动画速度
缺点:
  • 动画期间取消悬停时圆圈可见

示例

#hoverhandler, #shape, #round{
    height: 200px;
    width: 200px;
}
#hoverhandler #round{
    position: absolute;
    background-color:black;
    border-radius: 100%;
    transition: transform 2s .5s;
}
#hoverhandler #shape{
    transition: background-color 0s, clip-path 2s .5s;
    background-color: black;
    clip-path: path("M100,0 \
        C 203,0 219.97777228809804,126.4771161099481 175,166.14378277661478 \
        C 130.02222771190196,205.81044944328144 69.97777228809804,205.81044944328144 25,166.14378277661478 \
        C -19.977772288098038,126.4771161099481 -3,0 100,0Z")
}
#hoverhandler:hover #shape{
    transition: background-color 0s, clip-path 2s .4s;
    clip-path: path( "M100,0 \
        C 100,0 175,166.14378277661478 175,166.14378277661478 \
        C 175,166.14378277661478 25,166.14378277661478 25,166.14378277661478 \
        C 25,166.14378277661478 100,0 100,0Z");
}

#hoverhandler:hover #round{
    transform: scale(0);
    transition: transform 2s ease-in;
}
<div id="hoverhandler">
    <div id="round"></div>
    <div id="shape"></div>
</div>

如何计算坐标

// tested with symmetrical triangle
// height width
h=w= 200
// center of circle, radius
cx=cy=r= w/2
// width of the right/left half triangle
w_tri=w_tri_ri=w_tri_le=75
// length of the triangle from the center to bottom
h_tri_bo=h_tri_bo_ri=h_tri_bo_le=Math.sqrt(r**2-w_tri**2)//=Math.sqrt(r**2-w_tri_ri**2)=Math.sqrt(r**2-w_tri_le**2)

// untranslated coordinates
// console.log(cx,0)
// console.log(cx+w_tri_ri,cy+h_tri_bo_ri)
// console.log(cx-w_tri_le,cy+h_tri_bo_le)

// translated to be at the centre (centre of the circle translated to the origin)
console.log(0,-cy)
console.log(w_tri_ri,h_tri_bo)
console.log(-w_tri_le,h_tri_bo)

l = 68
add_top = 35

ratio = w_tri/h_tri_bo
x_bo= l/(Math.sqrt(ratio**2+1))
y_bo= x_bo/ratio


console.log("Top handles")
console.log(-l, -cy)
console.log(l, -cy)
console.log("Right handles")
console.log(w_tri+x_bo, h_tri_bo - y_bo)
console.log(w_tri-x_bo, h_tri_bo + y_bo)
console.log("Left handles", x_bo)
console.log(-w_tri+x_bo, h_tri_bo + y_bo)
console.log(-w_tri-x_bo, h_tri_bo - y_bo)

res = `M${0},${-cy}
    C ${l+add_top},${-cy} ${w_tri+x_bo},${h_tri_bo - y_bo} ${w_tri_ri},${h_tri_bo}
    C ${w_tri-x_bo},${h_tri_bo + y_bo} ${-w_tri+x_bo},${h_tri_bo + y_bo} ${-w_tri_le},${h_tri_bo}
    C ${-w_tri-x_bo},${h_tri_bo - y_bo} ${-l-add_top},${-cy} ${0},${-cy}Z`

res_transf = `M${cx},${0}
    C ${l+add_top+cx},${0} ${w_tri+x_bo+cx},${h_tri_bo - y_bo+cy} ${w_tri_ri+cx},${h_tri_bo+cy}
    C ${w_tri-x_bo+cx},${h_tri_bo + y_bo+cy} ${-w_tri+x_bo+cx},${h_tri_bo + y_bo+cy} ${-w_tri_le+cx},${h_tri_bo+cy}
    C ${-w_tri-x_bo+cx},${h_tri_bo - y_bo+cy} ${-l-add_top+cx},${0} ${cx},${0}Z`

res_transf_hov = `M${cx},${0}
    C ${cx},${0} ${w_tri_ri+cx},${h_tri_bo+cy} ${w_tri_ri+cx},${h_tri_bo+cy}
    C ${w_tri+cx},${h_tri_bo+cy} ${-w_tri+cx},${h_tri_bo+cy} ${-w_tri_le+cx},${h_tri_bo+cy}
    C ${-w_tri+cx},${h_tri_bo+cy} ${cx},${0} ${cx},${0}Z`


console.log(res_transf_hov)
//document.getElementById("shape").setAttribute("d",res_transf)


-1
投票

这可能是实现此效果的最简单方法。我们实际上使用的是遮罩而不是clipPath。我们从一个三角形作为遮罩开始,然后放大一个圆(也是遮罩的一部分),使其变得与三角形大小相同。

只要动画速度很快,这种方法就很有效。如果您想要较慢的动画,您可能需要采用使三角形变形的方法。

.tri-up
{
    background-color: red;
    width: 400px;
  height: 400px;
}

.tri-up:hover svg .circmask
{
    -webkit-transform: scale(2);
    transform: scale(2);
    -webkit-transition: -webkit-transform 0.1s;
    transition: transform 0.1s;
}

.tri-up svg .circmask
{
    -webkit-transform: scale(1);
    transform: scale(1);
    -webkit-transition: -webkit-transform 0.1s;
    transition: transform 0.1s;
}
<div class='tri-up'>
    <svg width="100%" height="100%" viewBox="-100 -100 200 200">
        <mask id="clipTriangleUp">
            <polygon points="0,-100, 87,50, -87,50" fill="white"/>
            <circle r="50" fill="white" class="circmask"/>
        </mask>
        <image mask="url(#clipTriangleUp)" 
               x="-100" y="-100" width="100%" height="100%" 
               xlink:href="http://placehold.it/400x400"/>
    </svg>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.