animate grid card overlay
This commit is contained in:
+97
-16
@@ -1,9 +1,12 @@
|
|||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"image/png"
|
||||||
"slices"
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
@@ -35,20 +38,29 @@ type coverImage struct {
|
|||||||
OnShowPage func()
|
OnShowPage func()
|
||||||
OnShowContextMenu func(fyne.Position)
|
OnShowContextMenu func(fyne.Position)
|
||||||
|
|
||||||
Im *ImagePlaceholder
|
Im *ImagePlaceholder
|
||||||
playbtn *canvas.Image
|
playbtn *canvas.Image
|
||||||
favoriteButton *canvas.Image
|
favoriteButton *canvas.Image
|
||||||
moreButton *canvas.Image
|
moreButton *canvas.Image
|
||||||
prevTheme fyne.ThemeVariant
|
gradient *canvas.LinearGradient
|
||||||
bottomPanel *fyne.Container
|
bottomPanel *fyne.Container
|
||||||
|
|
||||||
|
mouseInAnim *fyne.Animation
|
||||||
|
|
||||||
mouseInsidePlay bool
|
mouseInsidePlay bool
|
||||||
mouseInsideFav bool
|
mouseInsideFav bool
|
||||||
mouseInsideMore bool
|
mouseInsideMore bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
playBtnSize = fyne.NewSize(60, 60)
|
// N.B: Only one GridViewItem can be showing the play image
|
||||||
playBtnHoveredSize = fyne.NewSize(65, 65)
|
// at a time, so they can share this image.Image
|
||||||
|
playBtnImageSrc, playBtnImage image.Image
|
||||||
|
|
||||||
|
// opacity when unhovered
|
||||||
|
playBtnOpacity = float32(0.85)
|
||||||
|
playBtnSize = fyne.NewSquareSize(60)
|
||||||
|
playBtnHoveredSize = fyne.NewSquareSize(70)
|
||||||
|
|
||||||
resourcesInitted bool
|
resourcesInitted bool
|
||||||
heartFilledResource fyne.Resource
|
heartFilledResource fyne.Resource
|
||||||
@@ -66,6 +78,9 @@ func initResources() {
|
|||||||
}
|
}
|
||||||
resourcesInitted = true
|
resourcesInitted = true
|
||||||
|
|
||||||
|
playBtnImageSrc, _ = png.Decode(bytes.NewReader(res.ResPlaybuttonPng.StaticContent))
|
||||||
|
playBtnImage = image.NewNRGBA(playBtnImageSrc.Bounds())
|
||||||
|
|
||||||
inlineIconSize = fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameInlineIcon)
|
inlineIconSize = fyne.CurrentApp().Settings().Theme().Size(theme.SizeNameInlineIcon)
|
||||||
|
|
||||||
// TODO: replace util.ColorizeSVG with Fyne's canvas.ColorizeSVG once
|
// TODO: replace util.ColorizeSVG with Fyne's canvas.ColorizeSVG once
|
||||||
@@ -91,16 +106,18 @@ func newCoverImage(placeholderResource fyne.Resource) *coverImage {
|
|||||||
c.Im.OnTapped = c.Tapped
|
c.Im.OnTapped = c.Tapped
|
||||||
c.Im.OnTappedSecondary = c.TappedSecondary
|
c.Im.OnTappedSecondary = c.TappedSecondary
|
||||||
c.Im.ScaleMode = canvas.ImageScaleFastest
|
c.Im.ScaleMode = canvas.ImageScaleFastest
|
||||||
c.playbtn = &canvas.Image{FillMode: canvas.ImageFillContain, Resource: res.ResPlaybuttonPng}
|
c.playbtn = &canvas.Image{FillMode: canvas.ImageFillContain, Image: playBtnImage}
|
||||||
c.playbtn.SetMinSize(playBtnSize)
|
c.playbtn.SetMinSize(playBtnSize)
|
||||||
|
c.playbtn.ScaleMode = canvas.ImageScaleFastest
|
||||||
c.playbtn.Hidden = true
|
c.playbtn.Hidden = true
|
||||||
|
|
||||||
c.favoriteButton = canvas.NewImageFromResource(heartUnfilledResource)
|
c.favoriteButton = canvas.NewImageFromResource(heartUnfilledResource)
|
||||||
c.favoriteButton.SetMinSize(fyne.NewSquareSize(inlineIconSize))
|
c.favoriteButton.SetMinSize(fyne.NewSquareSize(inlineIconSize))
|
||||||
c.moreButton = canvas.NewImageFromResource(moreVerticalResource)
|
c.moreButton = canvas.NewImageFromResource(moreVerticalResource)
|
||||||
c.moreButton.SetMinSize(fyne.NewSquareSize(inlineIconSize))
|
c.moreButton.SetMinSize(fyne.NewSquareSize(inlineIconSize))
|
||||||
|
c.gradient = canvas.NewVerticalGradient(color.Transparent, color.Black)
|
||||||
c.bottomPanel = container.NewStack(
|
c.bottomPanel = container.NewStack(
|
||||||
canvas.NewVerticalGradient(color.Transparent, color.Black),
|
c.gradient,
|
||||||
container.NewVBox(
|
container.NewVBox(
|
||||||
layout.NewSpacer(), // keep the HBox pushed down
|
layout.NewSpacer(), // keep the HBox pushed down
|
||||||
container.NewHBox(
|
container.NewHBox(
|
||||||
@@ -165,14 +182,44 @@ func (c *coverImage) TappedSecondary(e *fyne.PointEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *coverImage) MouseIn(*desktop.MouseEvent) {
|
func (a *coverImage) MouseIn(*desktop.MouseEvent) {
|
||||||
a.playbtn.Hidden = false
|
|
||||||
a.updateFavoriteIcon(false)
|
a.updateFavoriteIcon(false)
|
||||||
a.favoriteButton.Hidden = !a.EnableFavorite
|
|
||||||
a.bottomPanel.Hidden = false
|
firstTick := true
|
||||||
a.Refresh()
|
a.mouseInAnim = fyne.NewAnimation(225*time.Millisecond, func(f float32) {
|
||||||
|
a.playbtn.Hidden = false
|
||||||
|
a.favoriteButton.Hidden = !a.EnableFavorite
|
||||||
|
a.bottomPanel.Hidden = false
|
||||||
|
|
||||||
|
if !a.mouseInsidePlay {
|
||||||
|
setPlayBtnTranslucency(f * float32(playBtnOpacity))
|
||||||
|
// else being controlled by other animation
|
||||||
|
}
|
||||||
|
|
||||||
|
t := float64(1 - f)
|
||||||
|
a.moreButton.Translucency = t
|
||||||
|
a.favoriteButton.Translucency = t
|
||||||
|
a.gradient.EndColor = color.RGBA{A: uint8(f * 255)}
|
||||||
|
|
||||||
|
if firstTick {
|
||||||
|
firstTick = false
|
||||||
|
a.Refresh()
|
||||||
|
} else {
|
||||||
|
a.playbtn.Refresh()
|
||||||
|
a.bottomPanel.Refresh()
|
||||||
|
}
|
||||||
|
if f == 1 {
|
||||||
|
a.mouseInAnim = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
a.mouseInAnim.Curve = fyne.AnimationEaseIn
|
||||||
|
a.mouseInAnim.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *coverImage) MouseOut() {
|
func (a *coverImage) MouseOut() {
|
||||||
|
if a.mouseInAnim != nil {
|
||||||
|
a.mouseInAnim.Stop()
|
||||||
|
a.mouseInAnim = nil
|
||||||
|
}
|
||||||
a.mouseInsidePlay = false
|
a.mouseInsidePlay = false
|
||||||
a.mouseInsideFav = false
|
a.mouseInsideFav = false
|
||||||
a.mouseInsideMore = false
|
a.mouseInsideMore = false
|
||||||
@@ -187,9 +234,21 @@ func (a *coverImage) MouseMoved(e *desktop.MouseEvent) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if in {
|
if in {
|
||||||
a.playbtn.SetMinSize(playBtnHoveredSize)
|
fyne.NewAnimation(canvas.DurationShort, func(f float32) {
|
||||||
|
t := (1-playBtnOpacity)*f + playBtnOpacity
|
||||||
|
setPlayBtnTranslucency(t)
|
||||||
|
delta := playBtnHoveredSize.Subtract(playBtnSize)
|
||||||
|
a.playbtn.SetMinSize(playBtnSize.Add(fyne.NewSquareSize(delta.Width * f)))
|
||||||
|
a.playbtn.Refresh()
|
||||||
|
}).Start()
|
||||||
} else {
|
} else {
|
||||||
a.playbtn.SetMinSize(playBtnSize)
|
fyne.NewAnimation(canvas.DurationShort, func(f float32) {
|
||||||
|
t := 1 - (1-playBtnOpacity)*f
|
||||||
|
setPlayBtnTranslucency(t)
|
||||||
|
delta := playBtnHoveredSize.Subtract(playBtnSize)
|
||||||
|
a.playbtn.SetMinSize(playBtnHoveredSize.Subtract(fyne.NewSquareSize(delta.Height * f)))
|
||||||
|
a.playbtn.Refresh()
|
||||||
|
}).Start()
|
||||||
}
|
}
|
||||||
a.playbtn.Refresh()
|
a.playbtn.Refresh()
|
||||||
a.mouseInsidePlay = in
|
a.mouseInsidePlay = in
|
||||||
@@ -282,6 +341,28 @@ func isInside(origin fyne.Position, radius float32, point fyne.Position) bool {
|
|||||||
return x*x+y*y <= radius*radius
|
return x*x+y*y <= radius*radius
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setPlayBtnTranslucency(f float32) {
|
||||||
|
size := playBtnImageSrc.Bounds().Size()
|
||||||
|
primary := theme.Color(theme.ColorNamePrimary).(color.NRGBA)
|
||||||
|
for x := 0; x < size.X; x++ {
|
||||||
|
for y := 0; y < size.Y; y++ {
|
||||||
|
r, _, _, a := playBtnImageSrc.At(x, y).RGBA()
|
||||||
|
// using the PNG source just as an alpha mask + white play triangle
|
||||||
|
c := primary
|
||||||
|
if r > 65000 {
|
||||||
|
// pixel is white (play triangle)
|
||||||
|
c = color.NRGBA{
|
||||||
|
R: 255,
|
||||||
|
G: 255,
|
||||||
|
B: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.A = uint8(float32(a) / 257 * f)
|
||||||
|
playBtnImage.(*image.NRGBA).SetNRGBA(x, y, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type GridViewItemModel struct {
|
type GridViewItemModel struct {
|
||||||
Name string
|
Name string
|
||||||
ID string
|
ID string
|
||||||
|
|||||||
Reference in New Issue
Block a user