Site Update in progress. Please excuse any potential mess. 😊
Menu

SVG Masking

abc

import { Dom } from "OneJS/Dom"
import { h, render } from "preact"
import { useEffect, useRef, useState } from "preact/hooks"
import { Color, ScriptableObject, Vector2 } from "UnityEngine"
import { Background, Painter2D, StyleBackground, VectorImage } from "UnityEngine/UIElements"

const axeImage = resource.loadImage("Images/wooden_axe.png")

const SomeShape = ({ overflow }) => {
    const ref = useRef<Dom>()

    useEffect(() => {
        var v = ScriptableObject.CreateInstance(VectorImage)
        var p = new Painter2D()
        paint(p)
        p.SaveToVectorImage(v)
        ref.current.ve.style.backgroundImage = new StyleBackground(Background.FromVectorImage(v))
    }, [])

    function paint(paint2D: Painter2D) {
        paint2D.strokeColor = Color.white
        paint2D.fillColor = Color.white
        paint2D.lineWidth = 10;
        paint2D.BeginPath()
        paint2D.MoveTo(new Vector2(0, 160))
        paint2D.LineTo(new Vector2(100, 0))
        paint2D.LineTo(new Vector2(200, 160))
        paint2D.ClosePath()
        paint2D.Fill()
    }

    return <div ref={ref} style={{ width: 200, height: 200, overflow: overflow ? "Hidden" : "Visible" }}>
        <image class="w-full h-full" image={axeImage} />
    </div>
}

const App = () => {
    const [overflow, setOverflow] = useState(false)

    return <div class="w-full h-full flex justify-center items-center">
        <SomeShape overflow={overflow} />
        <button onClick={() => setOverflow(!overflow)} text="Toggle" />
    </div>
}

render(<App />, document.body)

Ease-of-Use Functional Component based on above example

export interface MaskParams{image, path:Vector2[]}

export const MaskedImage=({image, path}:MaskParams)=>{
    var maskInstance = ScriptableObject.CreateInstance(VectorImage)
    var mask = new Painter2D()
    drawMask(mask)
    mask.SaveToVectorImage(maskInstance)
    
    function drawMask(mask:Painter2D){
        mask.BeginPath()
        path.map((pos, index)=>index == 0 ? mask.MoveTo(pos) : mask.LineTo(pos))
        mask.ClosePath()
        mask.Fill()
    }
    
    return(
        <div style={{backgroundImage: maskInstance, width:"100%", height:"100%", overflow:"Hidden"}}>    
            <div style={{width:"100%", height:"100%", backgroundImage:image}}/>
        </div>
    )
}

//Define a path like this
var maskPath:Vector2[]=[
        new Vector2(9,0), 
        new Vector2(96,0), 
        new Vector2(100, 4),
        new Vector2(100, 91),
        new Vector2(91, 100),
        new Vector2(4, 100),
        new Vector2(0, 96),
        new Vector2(0, 9)
    ]

//Implement Like this (if you want to change dimensions, style a div around it or implement a style property in its params)
render(<MaskedImage image={axeImage} mask={maskPath}/>, document.body)