Inner shadow on QML Rectangle

2019-04-30 20:22发布

问题:

How do I implement a Rectangle in QML with an inner shadow?

See example in link below:

Create inner shadow in UIView

UPDATE:

Here's a simplified version of what I'm trying to do (which does not show any shadow):

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
   width: 400
   height: 400

   Item {
      anchors.fill: parent

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

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

I'm sorry. My stupid... I got it wrong when i simplified the code (the Item was used for a DropShadow test, which works). This is how it was supposed to look like:

import QtQuick 2.0
import QtGraphicalEffects 1.0

Item {
   width: 400
   height: 400

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

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

回答1:

I'm not sure why, but it works if you use the item above the item you're trying cast a shadow within (in this case it just happens to be the root item, but it doesn't have to be):

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
    }
}

I got the idea from QTBUG-27213 when I was searching for related bugs.



回答2:

Here https://bugreports.qt.io/browse/QTBUG-56467 in first comment is the informative answer for those issues:

(c) Gunnar Sletta:

The InnerShadow will produce a shade in the opaque areas based on the translucent areas of the source object. When the source is fully opaque (like in the case of this rectangle, the result will thus be no shading.

DropShadow has a transparentBorder property which is set to true by default to aid the user in getting the expected results even for fully opaque input. The InnerShadow lacks this.

On a side note, the InnerShadow also uses the "old" gaussian code which requires significantly more samples to get the right

Anyway, the way do get an inner shadow on a rectangle is to pad it with a single pixel border of transparent pixels. This is also why it will sometimes work to give the parent of the intended source item as source of the effect. (Please note that this doesn't work in general, and that the parent is in fact unsupported as source as many GPUs will fail when you do this).

The following code produces the expected results:

import QtQuick 2.4
import QtGraphicalEffects 1.0

Item
{
    width: 320
    height: 480

    Item {
        anchors.centerIn: parent
        width: 100
        height: 100

        Rectangle {
            anchors.fill: parent
            anchors.margins: 1
            color: "lightsteelblue"
        }

        layer.enabled: true
        layer.effect: InnerShadow {
            color: "black"
            samples: 32
            radius: 10
            spread: 0.4
        }
    }
}

Alternatively, if you want to save the extra item, and can live with a tiny bit of trickery:

Rectangle {
    anchors.centerIn: parent
    width: 100
    height: 100
    color: "lightsteelblue"
    border.color: Qt.rgba(0, 0, 0, 0.01)
    border.width: 1
    layer.enabled: true
    layer.effect: InnerShadow {
        color: "black"
        samples: 20
        radius: 8.5
        spread: 0.2
    }
}

(the border.color, can't be 'transparent' because that is interpreted as no border)