2016-10-06 6 views
4

Внешний вид, который я пытаюсь использовать, - это сплошная кнопка цвета и текст на нем, как «Hello World», где текст полностью прозрачный, а фон - через кнопку.Как применить маску непрозрачности к элементу QML?

Иными словами, имея текст в виде маски прозрачности на элемент кнопки.

+0

Является ли это полностью пользовательские кнопки управления? Или вы используете Qt Quick Controls или аналогичные? – peppe

+0

Qt Quick Controls 1 или 2? – GrecKo

ответ

1

Я думаю, что это OpacityMask для.

Образец, показанный ниже, как представляется, иллюстрирует эффект, который вы ищете. Фон - сплошной красный Rectangle. На переднем плане находится сплошной синий цвет Rectangle. A Text объект, черный текст которого находится поверх синего переднего плана. OpacityMask использует Text в качестве маски для фона, что приводит к появлению красного текста.

import QtQuick 2.7 
import QtGraphicalEffects 1.0 

Rectangle { 
    width: 500 
    height: 500 

    Rectangle { 
     id: background 
     anchors.fill: parent 

     Rectangle { 
      id: left 
      anchors.top: parent.top 
      anchors.bottom: parent.bottom 
      anchors.left: parent.left 
      width: parent.width/2 
      color: "red" 

      ColorAnimation on color { 
       from: "red" 
       to: "green" 
       duration: 5000 
       loops: Animation.Infinite 
      } 
     } 

     Rectangle { 
      id: right 
      anchors.top: parent.top 
      anchors.bottom: parent.bottom 
      anchors.right: parent.right 
      width: parent.width/2 
      color: "red" 
     } 
    } 

    Rectangle { 
     id: foreground 
     anchors.fill: parent 
     color: "blue" 
    } 

    Text { 
     id: txt 
     anchors.centerIn: parent 
     text: "Test" 
     font.pointSize: 60 
     color: "black" 
    } 

    OpacityMask { 
     anchors.fill: txt 
     source: background 
     maskSource: txt 
    } 
} 

Обновление: Добавлен более сложный фон, чтобы лучше проиллюстрировать, что OpacityMask действительно вычислительном прорезать слой. Также добавлена ​​анимация, показывающая, что вычисленный разрез меняется со временем.

+0

Вы действительно протестировали это? Не похоже, что это приведет к требуемому результату, плюс вы не увидите «вокруг» черного цвета. – dtech

+0

Да, я запустил его в 'qmlscene', по общему признанию, из' 5.8.0-beta', но это должно быть нормально в '5.7'. Что касается недостающих котировок, исправил это ... интересно, 'qmlscene' не жаловался. – Tim

+0

Это не дает ожидаемого результата для меня. Существует один огромный синий прямоугольник с одним огромным размытым красным тестом внутри, но фактический фон не посевается. – dtech

5

Вот один из способов сделать это:

// TB.qml 
MouseArea { 
    width: txt.contentWidth + 20 
    height: txt.contentHeight + 10 
    property alias text: txt.text 
    property alias color: sh.color 
    ShaderEffect { 
    id: sh 
    anchors.fill: parent 
    property color color: "red" 
    property var source : ShaderEffectSource { 
     sourceRect: Qt.rect(0, 0, sh.width, sh.height) 
     sourceItem: Item { 
     width: sh.width 
     height: sh.height 
     Text { 
      id: txt 
      anchors.centerIn: parent 
      font.bold: true 
      font.pointSize: 30 
      text: "test" 
     } 
     } 
    } 
    fragmentShader: 
     "varying highp vec2 qt_TexCoord0; 
      uniform highp vec4 color; 
      uniform sampler2D source; 
      void main() { 
       gl_FragColor = color * (1.0 - texture2D(source, qt_TexCoord0).w); 
      }" 
    } 
} 

Использование его:

TB { 
    text: "HELLO WORLD!!!" 
    color: "red" 
    onClicked: console.log("hi world") 
    } 

Результат:

enter image description here

кнопка красного цвета, текст серый от серый фон, и он точно покажет все, что находится под кнопкой.

Очевидно, что кнопка рудиментарна, но пример outta будет достаточным, чтобы вы начали и реализовали что-то в соответствии с вашими потребностями.

Ключевым элементом здесь является пользовательский шейдер, который является очень простым: он раскрашивает каждый фрагмент и применяет маску как альфа. Очевидно, вы можете использовать ShaderEffectSource, чтобы превратить любой элемент QML в текстуру и заменить ShaderEffectSource на другой sampler 2D и смешать две текстуры любым способом, вырезать с помощью альфа-канала или любого из RGB, если вы используете оттенки серого маска. И в отличие от довольно ограниченного элемента OpacityMask, это фактически прорезает и показывает все, что находится под ним, как предполагается.

+0

Отличное решение :) – Tim

3

Что вы хотите, это OpacityMask, которые могут быть инвертированы. Это планируется в будущих версиях Qt (5.7.1 может быть?) И на самом деле вы уже можете взглянуть на него: https://github.com/qt/qtgraphicaleffects/blob/5.7.1/src/effects/OpacityMask.qml

Между тем вы можете скопировать код в файл с именем MyOpacityMask.qml или что-то другое и использования это так с Button из Qt Quick Controls 2:

Button { 
    id: button 
    text: "Yolo" 
    background.visible: false 
    contentItem.visible: false 
    contentItem.anchors.fill: button //can be avoided at the cost of creating another Item and ShaderEffectSource 
    MyOpacityMask { 
     anchors.fill: parent 
     invert: true 
     source: button.background 
     maskSource: button.contentItem 
    } 
} 

Это ранее было упомянуто в Opposite for OpacityMask

1

Вы можете достичь этого, используя layer вложенное свойство, как следовать без использования OpacityMask.
Кроме того, вы не каких-либо ограничений, и вы можете использовать любой элемент QML, используйте любой QtQuick.Controls и стиль это как обычно :)

result

Image { 
    id: bk 
    source: "http://l7.alamy.com/zooms/7b6f221aadd44ffab6a87c234065b266/sheikh-lotfollah-mosque-at-naqhsh-e-jahan-square-in-isfahan-iran-interior-g07fw2.jpg" 
} 

Button { 
    id: button 
    anchors.centerIn: bk 
    width: 210; height: 72 
    visible: true 
    opacity: 0.0 
    layer.enabled: true 
    layer.smooth: true 
    onClicked: console.log("Clicked") 
} 

Rectangle { 
    id: _mask 
    anchors.fill: button 
    color: "transparent" 
    visible: true 
    Text { 
     font { pointSize: 20; bold: true } 
     anchors.centerIn: parent 
     text: "Hello World!" 
    } 

    layer.enabled: true 
    layer.samplerName: "maskSource" 
    layer.effect: ShaderEffect { 
     property variant source: button 
     fragmentShader: " 
       varying highp vec2 qt_TexCoord0; 
       uniform highp float qt_Opacity; 
       uniform lowp sampler2D source; 
       uniform lowp sampler2D maskSource; 
       void main(void) { 
        gl_FragColor = texture2D(source, qt_TexCoord0.st) * (1.0-texture2D(maskSource, qt_TexCoord0.st).a) * qt_Opacity; 
       } 
      " 
    } 
}