Browse Source

Visual effects (#1547)

pull/1511/head
Nikolay Igotti 3 years ago committed by GitHub
parent
commit
834dc94e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      compose/frameworks/support
  2. 1
      examples/visual-effects/README.md
  3. 57
      examples/visual-effects/build.gradle.kts
  4. 1
      examples/visual-effects/gradle.properties
  5. BIN
      examples/visual-effects/gradle/wrapper/gradle-wrapper.jar
  6. 5
      examples/visual-effects/gradle/wrapper/gradle-wrapper.properties
  7. 185
      examples/visual-effects/gradlew
  8. 89
      examples/visual-effects/gradlew.bat
  9. 10
      examples/visual-effects/settings.gradle.kts
  10. 162
      examples/visual-effects/src/main/kotlin/RotatingWords.kt
  11. 255
      examples/visual-effects/src/main/kotlin/WaveEffect.kt
  12. 27
      examples/visual-effects/src/main/kotlin/main.kt
  13. BIN
      examples/visual-effects/src/main/resources/compose-community-primary.png
  14. 19
      examples/visual-effects/src/main/resources/compose-community-primary.svg

2
compose/frameworks/support

@ -1 +1 @@
Subproject commit e2a3108b92d4c54b5780f9eeceb1712845961cd7 Subproject commit f1e595b90421e4896a9164c3ae0a652920020b77

1
examples/visual-effects/README.md

@ -0,0 +1 @@
Several visual effects implmented with Compose Multiplatform, used in 1.0 release announce video.

57
examples/visual-effects/build.gradle.kts

@ -0,0 +1,57 @@
import org.jetbrains.compose.compose
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
// __KOTLIN_COMPOSE_VERSION__
kotlin("jvm") version "1.5.31"
// __LATEST_COMPOSE_RELEASE_VERSION__
id("org.jetbrains.compose") version "1.0.0"
}
group = "me.user"
version = "1.0"
repositories {
mavenCentral()
google()
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
dependencies {
implementation(compose.desktop.currentOs)
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "11"
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
compose.desktop {
application {
mainClass = "org.jetbrains.compose.demo.visuals.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "compose-demo"
packageVersion = "1.0.0"
}
}
}
afterEvaluate {
val additionalArguments = mutableListOf<String>()
val runTask = tasks.named<JavaExec>("run") {
this.args = additionalArguments
}
tasks.register("runWords") {
additionalArguments.add("words")
dependsOn(runTask)
}
tasks.register("runWave") {
additionalArguments.add("wave")
dependsOn(runTask)
}
}

1
examples/visual-effects/gradle.properties

@ -0,0 +1 @@
kotlin.code.style=official

BIN
examples/visual-effects/gradle/wrapper/gradle-wrapper.jar vendored

Binary file not shown.

5
examples/visual-effects/gradle/wrapper/gradle-wrapper.properties vendored

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
examples/visual-effects/gradlew vendored

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
examples/visual-effects/gradlew.bat vendored

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

10
examples/visual-effects/settings.gradle.kts

@ -0,0 +1,10 @@
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
}
rootProject.name = "visual-effects"

162
examples/visual-effects/src/main/kotlin/RotatingWords.kt

@ -0,0 +1,162 @@
package org.jetbrains.compose.demo.visuals
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.*
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.useResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.singleWindowApplication
@OptIn(ExperimentalAnimationApi::class, ExperimentalMaterialApi::class)
@Preview
@Composable
fun Words() {
val density = LocalDensity.current
val duration = 5000
val infiniteTransition = rememberInfiniteTransition()
val angle by infiniteTransition.animateFloat(
initialValue = -50f,
targetValue = 30f,
animationSpec = infiniteRepeatable(
animation = tween(duration, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Reverse
)
)
val scale by infiniteTransition.animateFloat(
initialValue = 1f,
targetValue = 7f,
animationSpec = infiniteRepeatable(
animation = tween(duration, easing = FastOutSlowInEasing),
repeatMode = RepeatMode.Reverse
)
)
val logoSvg = remember {
useResource("compose-community-primary.svg") { loadSvgPainter(it, density) }
}
val baseLogo = DpOffset(350.dp, 270.dp)
val baseText = DpOffset(350.dp, 270.dp)
val baseRu = DpOffset(100.dp, 100.dp)
val baseEn = DpOffset(100.dp, 600.dp)
val baseCh = DpOffset(600.dp, 100.dp)
val baseJa = DpOffset(600.dp, 600.dp)
val color1 = Color(0x6B, 0x57, 0xFF)
val color2 = Color(0xFE, 0x28, 0x57)
val color3 = Color(0xFD, 0xB6, 0x0D)
val color4 = Color(0xFC, 0xF8, 0x4A)
Column(modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Word(position = baseRu, angle = angle, scale = scale, text = "Ваш", color = color1)
Word(position = baseEn, angle = angle, scale = scale, text = "Your", color = color2)
Word(position = baseCh, angle = angle, scale = scale, text = "您的", color = color3)
Word(position = baseJa, angle = angle, scale = scale, text = "あなたの", color = color4)
Word(position = baseText, angle = 0f, scale = 6f, text = " Compose\nMultiplatform", color = Color(52, 67, 235),
alpha = 0.4f)
val size = 80.dp * scale
Image(logoSvg, contentDescription = "Logo",
modifier = Modifier
.offset(baseLogo.x - size / 2, baseLogo.y - size / 2)
.size(size)
.rotate(angle * 2f)
)
}
}
@Composable
fun Word(position: DpOffset, angle: Float, scale: Float, text: String,
color: Color, alpha: Float = 0.8f) {
Text(
modifier = Modifier
.offset(position.x, position.y)
.rotate(angle)
.scale(scale)
.alpha(alpha),
color = color,
fontWeight = FontWeight.Bold,
text = text,
)
}
@Composable
@Preview
fun FallingSnow() {
BoxWithConstraints(Modifier.fillMaxSize()) {
repeat(50) {
val size = remember { 20.dp + 10.dp * Math.random().toFloat() }
val alpha = remember { 0.10f + 0.15f * Math.random().toFloat() }
val sizePx = with(LocalDensity.current) { size.toPx() }
val x = remember { (constraints.maxWidth * Math.random()).toInt() }
val infiniteTransition = rememberInfiniteTransition()
val t by infiniteTransition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(16000 + (16000 * Math.random()).toInt(), easing = LinearEasing),
repeatMode = RepeatMode.Restart
)
)
val initialT = remember { Math.random().toFloat() }
val actualT = (initialT + t) % 1f
val y = (-sizePx + (constraints.maxHeight + sizePx) * actualT).toInt()
Box(
Modifier
.offset { IntOffset(x, y) }
.clip(CircleShape)
.alpha(alpha)
.background(Color.White)
.size(size)
)
}
}
}
@Composable
@Preview
fun Background() = Box(
Modifier
.fillMaxSize()
.background(Color(0xFF6F97FF))
)
@Composable
@Preview
fun RotatingWords() {
Background()
FallingSnow()
Words()
}

255
examples/visual-effects/src/main/kotlin/WaveEffect.kt

@ -0,0 +1,255 @@
package org.jetbrains.compose.demo.visuals
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerMoveFilter
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.WindowState
import kotlin.math.*
@Composable
fun WaveEffect(onCloseRequest: () -> Unit, showControls: Boolean) {
var windowState = remember { WindowState(width = 1200.dp, height = 800.dp) }
Window(onCloseRequest = {}, undecorated = true, transparent = true, state = windowState) {
Grid()
}
if (showControls) {
Window(
onCloseRequest = onCloseRequest,
state = WindowState(width = 200.dp, height = 400.dp, position = WindowPosition(1400.dp, 200.dp))
) {
Column {
SettingsPanel(State.red, "Red")
SettingsPanel(State.green, "Green")
SettingsPanel(State.blue, "Blue")
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
@Preview
fun Grid() {
var mouseX by remember { mutableStateOf(0) }
var mouseY by remember { mutableStateOf(0) }
var centerX by remember { mutableStateOf(1200) }
var centerY by remember { mutableStateOf(900) }
var vX by remember { mutableStateOf(0) }
var vY by remember { mutableStateOf(0) }
var time by remember { mutableStateOf(System.nanoTime()) }
var prevTime by remember { mutableStateOf(System.nanoTime()) }
if (State.mouseUsed) {
centerX = (centerX + vX * (time - prevTime) / 1000000000).toInt()
if (centerX < -100) centerX = -100
if (centerX > 2600) centerX = 2600
vX =
(vX * (1 - (time - prevTime).toDouble() / 500000000) + 10 * (mouseX - centerX) * (time - prevTime) / 1000000000).toInt()
centerY = (centerY + vY * (time - prevTime) / 1000000000).toInt()
if (centerY < -100) centerY = -100
if (centerY > 1800) centerY = 1800
vY =
(vY * (1 - (time - prevTime).toDouble() / 500000000) + 5 * (mouseY - centerY) * (time - prevTime) / 1000000000).toInt()
prevTime = time
}
Surface(
modifier = Modifier.fillMaxSize().padding(5.dp).shadow(3.dp, RoundedCornerShape(20.dp))
.pointerMoveFilter(onMove = { mouseX = it.x.toInt(); mouseY = it.y.toInt(); false; },
onEnter = { State.mouseUsed = true; false; }, onExit = { State.mouseUsed = false; false; }),
color = Color(0, 0, 0),
shape = RoundedCornerShape(20.dp)
) {
Box(Modifier.fillMaxSize()) {
var x = 10 // initial position
var y = 10 // initial position
val shift = 25
var evenRow = false
var pointerOffsetX = (centerX / 2)
var pointerOffsety = (centerY / 2)
while (y < 790) {
x = if (evenRow) 10 + shift else 10
while (x < 1190) {
var size: Int = size(x, y, pointerOffsetX, pointerOffsety)
var color = boxColor(x, y, time, pointerOffsetX, pointerOffsety)
Dot(size, Modifier.offset(x.dp, y.dp), color, time)
x += shift * 2
}
y += shift
evenRow = !evenRow
}
HighPanel(pointerOffsetX, pointerOffsety)
}
LaunchedEffect(Unit) {
while (true) {
withFrameNanos {
time = it
}
}
}
}
}
@Composable
fun HighPanel(mouseX: Int, mouseY: Int) {
Text(
"Compose",
androidx.compose.ui.Modifier.offset(270.dp, 600.dp).scale(7.0f).alpha(alpha(mouseX, mouseY, 270, 700)),
color = colorMouse(mouseX, mouseY, 270, 700),
fontWeight = FontWeight.Bold
)
Text(
"Multiplatform",
androidx.compose.ui.Modifier.offset(350.dp, 700.dp).scale(7.0f).alpha(alpha(mouseX, mouseY, 550, 800)),
color = colorMouse(mouseX, mouseY, 550, 800),
fontWeight = FontWeight.Bold
)
Text(
"1.0",
androidx.compose.ui.Modifier.offset(800.dp, 700.dp).scale(7.0f).alpha(alpha(mouseX, mouseY, 800, 800)),
color = colorMouse(mouseX, mouseY, 800, 800),
fontWeight = FontWeight.Bold
)
}
private fun alpha(mouseX: Int, mouseY: Int, x: Int, y: Int): Float {
var d = distance(mouseX, mouseY, x, y)
if (d > 450) return 0.0f
d = d / 450 - 0.1
return (1 - d * d).toFloat()
}
private fun colorMouse(mouseX: Int, mouseY: Int, x: Int, y: Int): Color {
var d = distance(mouseX, mouseY, x, y) / 450
val color1 = Color(0x6B, 0x57, 0xFF)
val color2 = Color(0xFE, 0x28, 0x57)
val color3 = Color(0xFD, 0xB6, 0x0D)
val color4 = Color(0xFC, 0xF8, 0x4A)
if (d > 1) return color1
if (d > 0.66) return balancedColor(3 * d - 2, color1, color2)
if (d > 0.33) return balancedColor(3 * d - 1, color2, color3)
return balancedColor(3 * d, color3, color4)
}
private fun balancedColor(d: Double, color1: Color, color2: Color): Color {
if (d > 1) return color1
if (d < 0) return color2
val red = ((color1.red * d + color2.red * (1 - d)) * 255).toInt()
val green = ((color1.green * d + color2.green * (1 - d)) * 255).toInt()
val blue = ((color1.blue * d + color2.blue * (1 - d)) * 255).toInt()
return Color(red, green, blue)
}
private fun distance(x1: Int, y1: Int, x2: Int, y2: Int): Double {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2).toDouble())
}
@Composable
fun Dot(size: Int, modifier: Modifier, color: Color, time: Long) {
Box(
modifier.rotate(time.toFloat() / (15 * 10000000)).clip(RoundedCornerShape((3 + size / 20).dp))
.size(width = size.dp, height = size.dp)
) {
Box(modifier = Modifier.fillMaxSize().background(color)) {
}
}
}
private fun size(x: Int, y: Int, mouseX: Int, mouseY: Int): Int {
val addSize = 3
var result = 5
if (y > 550 && x < 550) return result
if (y > 650 && x < 900) return result
var distance2 = sqrt((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY).toDouble()) / 200
val scale: Double = (if (distance2 < 1) {
addSize * (1 - distance2)
} else 0.toDouble())
result += (if (State.mouseUsed) round(7.5 * scale).toInt() else 0)
return result
}
private fun boxColor(x: Int, y: Int, time: Long, mouseX: Int, mouseY: Int): Color {
if (!State.mouseUsed) return Color.White
val color1 = Color(0x6B, 0x57, 0xFF)
val color2 = Color(0xFE, 0x28, 0x57)
val color3 = Color(0xFC, 0xF8, 0x4A)
val distance = sqrt(((x - mouseX) * (x - mouseX) + (y - mouseY) * (y - mouseY)).toDouble())
val fade = exp(-1 * distance * distance / 150000)
var c1 = sin(12 * distance / 450 - (time.toDouble() / (5 * 100000000)))
if (c1 < 0) c1 = 0.0
var c2 = sin(2 + 12 * distance / 450 - (time.toDouble() / (5 * 100000000)))
if (c2 < 0) c2 = 0.0
var c3 = sin(4 + 12 * distance / 450 - (time.toDouble() / (5 * 100000000)))
if (c3 < 0) c3 = 0.0
var color = Color.White
if (c1 <= 0) {
var d = c2 / (c2 + c3)
color = balancedColor(d, color2, color3)
} else if (c2 <= 0) {
var d = c3 / (c1 + c3)
color = balancedColor(d, color3, color1)
} else if (c3 <= 0) {
var d = c1 / (c1 + c2)
color = balancedColor(d, color1, color2)
}
return balancedColor(fade, color, Color.White)
}
internal class ColorSettings {
var enabled by mutableStateOf(true)
var waveLength by mutableStateOf(30.0)
var simple by mutableStateOf(true)
var period by mutableStateOf(80.0)
}
private class State {
companion object {
var red by mutableStateOf(ColorSettings())
var green by mutableStateOf(ColorSettings())
var blue by mutableStateOf(ColorSettings())
var mouseUsed by mutableStateOf(false)
}
}
@Composable
internal fun SettingsPanel(settings: ColorSettings, name: String) {
Row {
Text(name)
Checkbox(settings.enabled, onCheckedChange = { settings.enabled = it })
Checkbox(settings.simple, onCheckedChange = { settings.simple = it })
Slider(
(settings.waveLength.toFloat() - 10) / 90,
{ settings.waveLength = 10 + 90 * it.toDouble() },
Modifier.width(100.dp)
)
Slider(
(settings.period.toFloat() - 10) / 90,
{ settings.period = 10 + 90 * it.toDouble() },
Modifier.width(100.dp)
)
}
}

27
examples/visual-effects/src/main/kotlin/main.kt

@ -0,0 +1,27 @@
package org.jetbrains.compose.demo.visuals
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.application
import androidx.compose.ui.window.singleWindowApplication
fun mainWords() = singleWindowApplication(
title = "Compose Demo", state = WindowState(size = DpSize(830.dp, 830.dp))
) {
RotatingWords()
}
fun mainWave(controls: Boolean) = application {
WaveEffect(::exitApplication, controls)
}
fun main(args: Array<String>) {
if (args.isEmpty()) return mainWords()
when (val effect = args[0]) {
"words" -> mainWords()
"wave" -> mainWave(false)
"wave-controls" -> mainWave(true)
else -> throw Error("Unknown effect: $effect")
}
}

BIN
examples/visual-effects/src/main/resources/compose-community-primary.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

19
examples/visual-effects/src/main/resources/compose-community-primary.svg

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 600 600" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
style="fill-rule:evenodd;clip-rule:evenodd;"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<path d="M301.214,418.529C300.972,418.54 300.73,418.556 300.488,418.558C297.091,418.587 293.745,417.722 290.787,416.051L222.6,377.537C220.635,376.43 218.996,374.823 217.85,372.88C216.703,370.937 216.088,368.727 216.067,366.471L216.066,288.156C216.058,287.32 216.093,286.49 216.172,285.673C216.377,283.54 216.909,281.503 217.707,279.602L199.294,268.265L177.742,256.191C175.719,260.427 174.734,265.233 174.784,270.219L174.785,387.053C174.848,393.894 178.571,400.201 184.531,403.561L286.256,461.02C290.669,463.512 295.661,464.802 300.729,464.76C300.911,464.758 301.092,464.742 301.274,464.738C301.245,449.844 301.217,439.234 301.217,439.234L301.214,418.529Z"
style="fill:rgb(4,22,25);fill-rule:nonzero;"/>
<path d="M409.451,242.913L312.64,188.233C303.643,183.15 292.581,183.256 283.683,188.511L187.919,245.004C183.313,247.725 179.93,251.616 177.75,256.173L177.742,256.191L199.295,268.265L217.707,279.602C217.827,279.317 217.922,279.019 218.055,278.741C218.238,278.359 218.433,277.984 218.641,277.617C219.056,276.883 219.523,276.179 220.04,275.511C221.365,273.798 223.005,272.353 224.871,271.254L289.062,233.387C290.422,232.587 291.874,231.955 293.387,231.507C295.529,230.865 297.765,230.598 299.998,230.72C302.976,230.885 305.876,231.734 308.473,233.201L373.366,269.853C375.543,271.084 377.493,272.678 379.134,274.565C379.676,275.19 380.182,275.847 380.647,276.531C380.855,276.837 381.049,277.151 381.24,277.467L397.794,266.392L420.344,252.925L420.313,252.876C417.547,248.799 413.767,245.354 409.451,242.913Z"
style="fill:rgb(55,191,110);fill-rule:nonzero;"/>
<path d="M381.24,277.467C381.513,277.917 381.773,278.375 382.008,278.844C382.208,279.243 382.395,279.648 382.566,280.059C382.909,280.881 383.193,281.726 383.412,282.588C383.74,283.881 383.921,285.214 383.935,286.568L383.934,361.095C383.956,363.946 383.351,366.768 382.162,369.36C381.934,369.859 381.693,370.35 381.424,370.827C379.75,373.793 377.322,376.267 374.388,377.997L310.197,415.871C307.466,417.48 304.381,418.393 301.214,418.529L301.217,439.234C301.217,439.234 301.245,449.844 301.274,464.738C306.103,464.608 310.907,463.296 315.213,460.75L410.976,404.249C419.875,398.997 425.314,389.366 425.216,379.033L425.218,267.849C425.165,262.483 423.34,257.343 420.344,252.925L397.794,266.392L381.24,277.467Z"
style="fill:rgb(56,112,178);fill-rule:nonzero;"/>
<path d="M177.75,256.173C179.93,251.616 183.313,247.725 187.919,245.004L283.683,188.511C292.581,183.256 303.643,183.149 312.64,188.233L409.451,242.913C413.767,245.353 417.547,248.799 420.313,252.876L420.344,252.925L498.594,206.193C494.029,199.463 487.79,193.776 480.666,189.747L320.864,99.49C306.013,91.099 287.753,91.274 273.066,99.95L114.994,193.199C107.391,197.691 101.807,204.112 98.209,211.634L177.742,256.191L177.75,256.173ZM301.274,464.738C301.092,464.742 300.911,464.758 300.729,464.76C295.661,464.802 290.669,463.512 286.256,461.02L184.531,403.562C178.572,400.202 174.848,393.894 174.785,387.053L174.784,270.219C174.734,265.233 175.719,260.427 177.742,256.191L98.209,211.635C94.86,218.633 93.229,226.578 93.312,234.821L93.314,427.671C93.424,438.973 99.544,449.374 109.402,454.922L277.313,549.765C284.598,553.879 292.837,556.008 301.203,555.938L301.205,555.803C301.393,543.776 301.333,495.262 301.274,464.738Z"
style="fill:none;fill-rule:nonzero;stroke:rgb(8,48,66);stroke-width:10px;"/>
<path d="M498.594,206.194L420.344,252.925C423.341,257.343 425.165,262.483 425.218,267.849L425.216,379.033C425.314,389.366 419.875,398.997 410.976,404.249L315.213,460.751C310.907,463.296 306.103,464.609 301.274,464.738C301.333,495.262 301.393,543.776 301.205,555.802L301.203,555.938C309.475,555.871 317.736,553.68 325.111,549.321L483.183,456.056C497.872,447.387 506.85,431.489 506.688,414.433L506.691,230.908C506.604,222.018 503.573,213.503 498.594,206.194Z"
style="fill:none;fill-rule:nonzero;stroke:rgb(8,48,66);stroke-width:10px;"/>
<path d="M301.203,555.938C292.837,556.007 284.598,553.878 277.313,549.764L109.402,454.921C99.545,449.374 93.424,438.973 93.314,427.671L93.312,234.821C93.23,226.578 94.86,218.633 98.209,211.634C101.807,204.112 107.391,197.691 114.994,193.199L273.066,99.949C287.753,91.274 306.013,91.098 320.864,99.49L480.666,189.747C487.79,193.776 494.029,199.463 498.594,206.193C503.573,213.503 506.604,222.018 506.691,230.908L506.688,414.433C506.85,431.489 497.872,447.387 483.183,456.056L325.111,549.32C317.736,553.68 309.475,555.871 301.203,555.938Z"
style="fill:none;fill-rule:nonzero;stroke:rgb(8,48,66);stroke-width:10px;"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

Loading…
Cancel
Save