利用Clip制作打洞效果

起因

如上篇博文所说,连线原型需要在中间文字上下各留15像素的空白。设计师完成原型之后,问我有没有办法实现,我说,我能想到两种实现方式。其中一种就是上篇博文所说的OpacityMask。第二种就是使用Clip了。下面是效果图:

代码实现

Clip

Clip定义在UIElement中,类型为Geometry 。MSDN中的解释是获取或设置用于定义元素内容边框的几何图形。实际上不光可以在边框处留住空白,在UI元素里面留出空白也是可以的,只要定义好相关的形状。

矩形空洞

在一个大矩形中去除一个小矩形就行了。

主要代码在RectangleHoleConverter中,代码如下:

namespace HoleWithClip
{
    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;

    /// <summary>
    /// 矩形空洞的转换器
    /// </summary>
    public class RectangleHoleConverter : IMultiValueConverter
    {
        /// <summary>
        /// 转换成矩形空洞
        /// </summary>
        /// <param name="values">
        /// 转换值列表,第一个表示起始宽度,第二个表示起始高度,
        /// 第三个表示总宽度,第四个表示总高度
        /// 第五个表示宿主宽度,第六个表示宿主宽度
        /// </param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length != 6 || values.HaveNullItem()
                || !values.IsAllInstanceOfType(typeof(double)))
            {
                return DependencyProperty.UnsetValue;
            }

            var maskStartWidth = (double)values[0];
            var maskStartHeight = (double)values[1];
            var maskTotalWidth = (double)values[2];
            var maskTotalHeight = (double)values[3];
            var hostWidth = (double)values[4];
            var hostHeight = (double)values[5];
            if (hostWidth == 0.0 || hostHeight == 0.0)
            {
                return null;
            }

            var maskRectangle = new RectangleGeometry(new Rect(new Size(hostWidth, hostHeight)));
            var maskEllipse = new RectangleGeometry(new Rect(
                new Point(maskStartWidth, maskStartHeight),
                new Size(maskTotalWidth, maskTotalHeight)));
            var combinedGeometry = Geometry.Combine(maskRectangle, maskEllipse, GeometryCombineMode.Exclude, null);

            return combinedGeometry;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new[] { Binding.DoNothing };
        }
    }
}

其中用到了两个扩展方法如下:

namespace HoleTest
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public static class EnumerableExtension
    {
        /// <summary>
        /// 枚举器中是否存在null条目
        /// </summary>
        /// <typeparam name="T">元素类型</typeparam>
        /// <param name="enumerable">元素枚举</param>
        /// <returns>存在null条目返回true,否则返回false</returns>
        public static bool HaveNullItem<T>(this IEnumerable<T> enumerable)
        {
            return enumerable.Any(item => item == null);
        }

        /// <summary>
        /// 枚举器中是否全为指定类型的实例
        /// </summary>
        /// <typeparam name="T">元素类型</typeparam>
        /// <param name="enumerable">元素枚举</param>
        /// <returns>全为指定类型的实例返回true,否则返回false</returns>
        public static bool IsAllInstanceOfType<T>(this IEnumerable<T> enumerable, Type type)
        {
            return enumerable.All(item => type.IsInstanceOfType(item));
        }
    }
}

椭圆形空洞

跟矩形空洞类似,在大的矩形中间排除一个椭圆。

主要代码在EllipseHoleConverter中,代码如下:

namespace HoleWithClip
{
    using System;
    using System.Globalization;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Media;

    /// <summary>
    /// 椭圆形空洞的转换器
    /// </summary>
    public class EllipseHoleConverter : IMultiValueConverter
    {
        /// <summary>
        /// 转换成矩形空洞
        /// </summary>
        /// <param name="values">
        /// 转换值列表,第一个表示起始宽度,第二个表示起始高度,
        /// 第三个表示总宽度,第四个表示总高度
        /// 第五个表示宿主宽度,第六个表示宿主宽度
        /// </param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length != 6 || values.HaveNullItem()
                || !values.IsAllInstanceOfType(typeof(double)))
            {
                return DependencyProperty.UnsetValue;
            }

            var maskEllipseCenterX = (double)values[0];
            var maskEllipseCenterY = (double)values[1];
            var maskRadiusX = (double)values[2];
            var maskRadiusY = (double)values[3];
            var hostWidth = (double)values[4];
            var hostHeight = (double)values[5];
            if (hostWidth == 0.0 || hostHeight == 0.0)
            {
                return null;
            }

            var maskRectangle = new RectangleGeometry(new Rect(new Size(hostWidth, hostHeight)));
            var maskEllipse = new EllipseGeometry(
                new Point(maskEllipseCenterX, maskEllipseCenterY),
                maskRadiusX,
                maskRadiusY);
            var combinedGeometry = Geometry.Combine(maskRectangle, maskEllipse, GeometryCombineMode.Exclude, null);

            return combinedGeometry;
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new[] { Binding.DoNothing };
        }
    }
}

不规则空洞

我们各种Shape中最常用的是Path,因为许多复杂的效果能够在设计工具中绘制成Path。与此类似,Geometry中最强大的还是PathGeometry。上面图片中的五角形就是将Path数据转换成PathGeometry的。XAML代码如下:

<Border Width="180"
        Height="180"
        Margin="3"
        Background="Aquamarine">
    <Border Background="LightPink" Clip="M90.000003,0.5 L111.12693,68.873594 L179.50001,68.871913 L124.18408,111.12744 L145.31405,179.49999 L90.000003,137.24175 L34.685959,179.49999 L55.815923,111.12744 L0.50000013,68.871913 L68.87308,68.873594 z" />
</Border>

话说,只看XAML代码真看不出这是什么鬼东西。

下载链接

博客园:HoleWithClip

OpacityMask与Clip区别

可以看出,通过OpacityMask和Clip都能实现打洞效果。但是通过OpactiyMask实现的效果在空洞中间会引发相应的事件,而Clip则不会,原因是在于UIElement中作命中测试考虑到了Clip的存在。应根据实际需求选择不同的实现方式。

时间: 2024-10-15 21:05:10

利用Clip制作打洞效果的相关文章

利用OpacityMask制作打洞效果

起因 项目上存在一个连线功能,在设计的原型中,在连线中间文字上下各有15像素的空白.接手的同事觉得没思路,问我能不能在不影响连线后面的背景情况下解决该问题.我就抽了点时间给他写了个Demo.回家后趁热打铁,重新写了个Demo,添加和完善了些功能.下面是效果图: 代码实现 OpacityMask 在最开始看到效果图的时候,我就想到利用OpacityMask来解决问题.可能这个属性平时很多朋友都没注意到,因为一般情况下用Opacity就足够了. OpacityMask定义在UIElement中,类型

利用css3制作毛玻璃的效果

忙里偷闲,最近又在看许多比较酷炫的效果.现在基于jquery的插件比较多,但是很多插件的兼容性不是太好,所以原生的才是王道.在日常当中,毛玻璃已经不常见了,那是一个很久远年代的东西了.诺,下面就是毛玻璃: 哈哈,不闲扯了,接下来不如正题吧,先看一下终极效果: 嗯,好了,,开始我们的步骤吧: 第一步:进行页面的基本搭建: 我在body上设置了一张大大的背景图,然后中间部分是一个div,html代码如下: <body> <div > iPhone 7 dramatically impr

利用Viewpager制作滚动游标效果

由之前几篇文章的了解,我懂得了图片的各种变换效果,和动画的效果,因此我做了下面的游标效果 附上代码: activity: package com.example.ui; import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android

利用replaceChild制作简单的吞噬效果【jsDEMO】

[功能说明] 利用replaceChild制作简单的吞噬效果 [HTML代码说明] <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in"

利用FluidMoveBehavior制作出手机通讯录平滑的效果

最近学习Blend,原来Blend制作动画等效果非常棒.下面演示一下FluidMoveBehavior应用,利用Blend中行为中的FluidMoveBehavior制作出手机通讯录平滑的效果 1.在画布上添加一个ListBox,添加N多个<TextBlock>,如图 2.在ListBox上右键按图指示操作创建副本 3.在模板中选择ItemsPresenter, 4.然后从行为面板中选择FluidMoveBehavior拖放到画板上的ItemsPresenter对象上 5.返回到ListBox

利用removeChild制作简单的倒序删除效果【jsDEMO】

[功能说明] 利用removeChild制作简单的倒序删除效果 [HTML代码说明] <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in"

前端素材解析—利用linear制作复杂的边框效果

在网上看到一种利用linear-gradient属性制作绚丽边框效果的方法.首先给出代码,大家可以在自己的电脑中查看效果: <!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="utf-8" /> <title></title> <style

利用insertBefore制作简单的循环插空效果【jsDEMO】

[功能说明] 利用insertBefore制作简单的循环插空效果 [HTML代码说明] <ul class="list" id="list"> <li class="in">1</li> <li class="in">2</li> <li class="in">3</li> <li class="in&quo

利用TabHost制作QQ客户端标签栏效果(低版本QQ)

学习一定要从基础学起,只有有一个好的基础,我们才会变得更加的perfect 下面小编将利用TabHost制作QQ客户端标签栏效果(这个版本的QQ是在前几年发布的)…. 首先我们看一下效果: 看到这个界面,刹那间一点喜悦感都没有了,不过对于我们学习程序的人来说,UI是一方面,代码也是一方面,今天讲述的是代码,所以我们就在此忽略UI吧 --------------------------------------------------------华丽分割线----------------------