QML 内阴影效果

QML Inner Shadow effect

我想在 QML 中创建一个带有内部阴影的矩形,类似于 Photoshop 所做的:

QMLInnerShadow 但我无法实现这种效果。我得到的最接近的是这个

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
    id: root
    width: 300
    height: 300

    Rectangle {
        id: myRectangle
        anchors.centerIn: parent
        width: 100
        height: 100
        color: "grey"
    }

    InnerShadow {
        anchors.fill: root
        cached: true
        horizontalOffset: 0
        verticalOffset: 0
        radius: 16
        samples: 32
        color: "#b0000000"
        smooth: true
        source: root
    }
}

这是我从 this post 那里得到的想法。但是,此示例仅在 root 的大小明显大于 myRectangle 的情况下才有效,而我不希望这样。我需要例如一个 200x10 正方形,其中阴影均匀分布在矩形的边缘。我为 InnerShadow 属性尝试了各种值,但我无法接近我想要的效果。

这可以使用 QML 实现吗?

"correct"方法-需要引号-使用效果应该是这个:

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
   id: root
   width: 300
   height: 300

   Item {
       id: src
       anchors.fill: parent

       Rectangle {
           id: myRectangle
           anchors.centerIn: parent
           width: 100
           height: 100
           color: "grey"
       }
   }

   InnerShadow {
       anchors.fill: src
       cached: true
       horizontalOffset: 0
       verticalOffset: 0
       radius: 16
       samples: 32
       color: "#b0000000"
       smooth: true
       source: src
   }
}

如您所见,它与另一个问题中提出的解决方案略有不同。使用此代码,您仍然需要保留 2 个像素才能产生效果,从而产生白色边框(或任何背景颜色)。通过将 root 更改为 Rectangle.

可以轻松解决此问题

最终示例解决方案如下。显然,您可以提取 root 组件(和相关的子组件)并将其放在 Component 或不同的 .qml 文件中供以后使用。

import QtQuick 2.4
import QtQuick.Window 2.2
import QtGraphicalEffects 1.0

Window {
    width: 200
    height: 20
    visible: true

    Rectangle {     // was Item
        id: root
        anchors.fill: parent
        color: "grey"


        Item {
            id: src
            anchors.fill: parent

            Rectangle {
                id: myRectangle
                anchors.centerIn: parent
                width: root.width - 2
                height: root.height - 2
                color: "lightgrey"
            }
        }

        InnerShadow {
            anchors.fill: src
            cached: true
            horizontalOffset: 0
            verticalOffset: 0
            radius: 16
            samples: 32
            color: "#b0000000"
            smooth: true
            source: src
        }
    }
}

最终代码示例的结果 window:

InnerShadow 元素类似于 QML 中的大多数图形效果元素和 GLSL 着色器,它查找边缘的方式是寻找透明和非透明之间的过渡。如果将滤镜应用于完全实心的图形元素,它不会找到任何边缘,因此也不会找到阴影。这与 Photoshop 滤镜的工作方式完全相同,它还通过扫描从透明到不透明的边缘来找到边缘(如您提供的示例所示)。它可以将图形区域的边缘视为隐式边缘,但这会大大限制可用性。这是 InnerShadow. Which again uses FastInnerShadow or GaussianInnerShadow 的来源,具体取决于 fast 属性。

如果您想要一个可以添加到现有元素的实现,而不必关心透明和非透明边缘之间的过渡,您可以使用这个:

Rectangle {
    id: myRectangle
    anchors.centerIn: parent
    width: 300
    height: 300
    color: "#AAAAAA"
}
Item {
    id: _innerShadow
    property var shadowSource: myRectangle
    property var color: "#B0000000"
    anchors.fill: shadowSource
    Item {
        id: _shadowMarker
        //width: source.width+1; height: source.height+1
        anchors.fill: parent;
        anchors.margins: -10
        ColorOverlay {
            anchors.fill: _shadowMarker;
            anchors.margins: 10
            source: _innerShadow.shadowSource
            color: "#00000000"
        }
        visible: false
    }
    InnerShadow {
        anchors.fill: _shadowMarker
        cached: true
        horizontalOffset: 0
        verticalOffset: 0
        radius: 16
        samples: 32
        color: _innerShadow.color
        smooth: true
        source: _shadowMarker
    }
}

这是我的解决方案

GoodInnerShadow.qml

///
/// Same as InnerShadow QML type, with the following differences
///
/// InnerShadow requires transparent space to be surrounding the 
/// item that you want to make an inner shadow for. GoodInnerShadow
/// does not require this.
///
/// InnerShadow draws the source with the shadow. GoodInnerShadow 
/// draws just the shadow
///
import QtQuick 2.15
import QtGraphicalEffects 1.0

Item {
    id: root

    anchors.centerIn: source
    width: source.width
    height: source.height
    required property var source
    property color color: "#50ffffff"
    property double radius: 12
    property double spread: .8

    Item{
        id: sourceMaskWithPadding
        visible: false

        anchors.centerIn: parent
        width: root.source.width + shadowOfInverse.samples * 2
        height: root.source.height + shadowOfInverse.samples * 2
        OpacityMask {
            id: sourceMask
            anchors.centerIn: parent
            width: root.source.width
            height: root.source.height
            maskSource: root.source
            source: root.source
        }
    }
    
    Rectangle {
        id: coloredRect
        visible: false

        color: root.color
        anchors.fill: sourceMaskWithPadding
    }

    OpacityMask {
        id: sourceInverse
        visible: false

        anchors.fill: coloredRect
        source: coloredRect
        maskSource: sourceMaskWithPadding
        invert: true
    }

    DropShadow {
        id: shadowOfInverse
        visible: false
        
        anchors.fill: sourceInverse
        source: sourceInverse
        radius: root.radius
        samples: radius * 2 + 1
        color: root.color
        spread: root.spread
    }
    
    OpacityMask {
        id: sourceInnerShadow
        anchors.fill: sourceMaskWithPadding
        maskSource: sourceMaskWithPadding
        source: shadowOfInverse
    }

}

示例用法

import QtQuick 2.15

Item {
    width: 400
    height: 300

    Rectangle {
        id: myRect
        anchors.centerIn: parent
        width: 300
        height: 100
        color: "lightgray"
    }

    GoodInnerShadow {
        source: myRect
        color: "#aa000000"
        spread: .5
        radius: 16
    }
}

结果