LeaferJS 发布全新动画、状态、过渡、游戏功能
2024-09-20 "圆你游戏梦"
前言
记得很早就有很多同学说:“LeaferJS 这么好用,很想用它来开发游戏试试”。 当时我只是笑了笑,心想:“我们离做游戏还早着呢”。
尽管我们没有为游戏开发做过专门支持,但用户的好奇心是挡不住的。陆续有人用 LeaferJS 开发了一些有趣的小游戏,并在社区分享。比如 @子洋 开发了一系列的小游戏,还专门写了小游戏教程 《Leafer 开发小游戏 - 拼图》,那个时候我还没有特别重视起来。
直到有一天,有位同学 @hhzzcc 为了实现自己的游戏梦想,在功能还不齐全的情况下,硬是用 LeaferJS 开发了一款 超级玛丽游戏, 他还写了一篇教程 《我用 JS 开发一款超级玛丽游戏》,被很多平台转载,传播甚广。这时我才意识到, 用户是多么希望用 LeaferJS 来开发游戏啊。
因此我加快了步伐,全神贯注地开发了一个多月,带来了这一期的游戏、动画、状态和过渡功能更新,希望能帮助大家更轻松地开发小游戏、动画、UI 控件。
概述
这次更新主要包含了四大主要板块。我会先讲解动画、状态和过渡这三个基础支撑板块,然后再介绍游戏板块,它提供了一个类似游戏精灵的 Robot 元素。
接下来,我将通过示例和代码详细展示这些功能能够实现什么效果,以及它们带来了哪些新的体验。请先欣赏一段路径动画的示例:
import { Group, Leafer, Path, Polygon } from 'leafer-ui'
import '@leafer-in/animate'
import '@leafer-in/motion-path'
const leafer = new Leafer({ view: window })
const group = new Group()
const path = new Polygon({
x: 100,
y: 100,
motionPath: true, // 设置为运动路径,该 Group 内的其他元素都可以沿此路径运动
points: [0, 90, 100, 60, 200, 80, 300, 40, 375, 50, 450, 10, 550, 90, 550, 90, 0, 90],
curve: true,
fill: '#32cd79',
})
const car = new Path({
scale: 0.03,
fill: 'black',
around: 'bottom',
path: 'M949.586 629.224c-2.703-2.661-4.71-7.055-5.077-10.84-1.301-13.259-1.911-26.584-2.432-39.902-0.255-6.38-1.968-9.879-9.398-10.426-9.201-0.682-18.271-3.044-27.381-4.795-8.772-1.684-14.224-8.514-12.708-15.699 1.616-7.613 8.673-11.886 17.328-10.39 9.422 1.626 18.832 3.33 28.224 4.998 2.266-8.837-0.482-14.764-9.666-20.748-26.418-17.236-55.258-29.587-85.431-37.895-33.994-9.363-68.698-16.141-103.043-24.263-3.719-0.877-7.643-2.533-10.544-4.943-38.731-32.201-80.555-59.555-125.84-81.76-39.617-19.423-82.145-25.662-125.45-28.424-46.553-2.969-92.87-0.148-138.957 6.854-21.555 3.271-41.424 11.358-59.755 22.592-28.399 17.408-56.715 35.075-83.999 54.15-15.036 10.513-29.739 14.978-47.548 10.168-8.07-2.178-16.586-2.709-24.894-4.004-10.75-1.676-18.04 2.278-19.666 10.692-1.759 9.111 3.546 15.686 14.425 17.856 2.131 0.424 4.27 0.775 6.402 1.152 0.104 0.489 0.201 0.969 0.291 1.452-8.561 3.113-17.05 6.419-25.686 9.297-19.17 6.413-21.497 9.439-21.862 29.792-0.054 2.82-0.008 5.64-0.008 9.442 12.299 0 23.845-0.052 35.396 0.011 9.849 0.052 16.351 5.089 16.69 12.78 0.358 7.993-6.497 14.054-16.427 14.179-11.541 0.145-23.085 0.034-35.627 0.034 0 15.755-0.01 30.453 0 45.163 0.012 7.541-0.179 15.08 0.149 22.604 0.238 5.251-1.482 8.45-5.623 12.222-4.171 3.805-6.077 10.349-8.244 15.977-0.971 2.52 0.017 5.747-0.198 8.628-1.697 23.276 9.365 28.266 26.937 29.975 6.879 0.668 13.728 1.563 21.403 2.45-2.97-44.323 10.278-81.655 43.859-110.492 24.57-21.091 53.398-30.794 85.846-29.73 24.153 0.786 46.087 8.406 65.902 21.902 20.16 13.718 34.877 32.001 44.318 54.527 9.258 22.093 12.073 44.983 8.279 69.174H660.11c-5.864-48.677 9.189-89.301 47.71-119.794 26.652-21.102 57.782-28.826 91.558-25.087 60.437 6.677 122.165 65.992 107.31 150.752 12.938-2.104 25.373-3.916 37.706-6.305 2.579-0.503 5.102-2.809 7.047-4.865 9.141-9.619 7.555-39.181-1.855-48.461zM275.612 469.297c28.838-41.323 63.854-70.249 113.867-78.948 2.196 29.673 4.375 59.214 6.583 89.087-40.444-3.403-79.805-6.716-120.45-10.139z m179.897 71.126c-5.494 0.317-11.013 0.055-16.524 0.055v0.078c-5.506 0-11.013 0-16.527-0.003-0.575 0-1.154-0.039-1.73-0.13-7.911-1.247-13.83-7.818-13.216-14.659 0.61-6.893 6.516-12.017 14.546-12.153 11.013-0.181 22.046-0.3 33.051-0.011 8.454 0.211 14.175 5.82 14.375 13.248 0.195 7.312-5.532 13.081-13.975 13.575z m-30.098-58.221c-2.338-32.026-4.608-63.141-6.888-94.362 103.429-11.606 190.875 22.776 269.354 89.534-87.844 19.856-174.859 9.391-262.466 4.828zM236.91 570.114c-49.771-0.016-90.786 39.783-92.253 86.639-1.697 54.596 43.161 96.574 91.545 95.173 51.424-0.008 91.141-40.06 91.079-91.851-0.053-49.787-40.393-89.944-90.371-89.961z m-0.131 125.312c-17.785 0.532-34.806-15.951-35.104-33.997-0.307-18.086 15.924-34.542 34.257-34.748 18.157-0.192 34.65 16.176 34.635 34.365-0.024 17.843-15.75 33.839-33.788 34.38zM787.66 569.923c-53.395-1.601-95.199 41.641-94.92 90.003 0.29 51.36 39.759 91.973 90.084 91.992 52.573 0.038 92.121-39.199 92.188-91.492 0.07-48.553-40.287-89.095-87.352-90.503z m-3.893 125.425c-18.762-0.355-34.274-16.08-34.2-34.674 0.072-18.189 16.564-34.166 34.977-33.883 18.123 0.27 33.8 16.151 33.739 34.193-0.055 18.402-16.424 34.702-34.516 34.364z',
animation: { // 沿 path 运动至 100%
style: { motion: { type: "percent", value: 1 } },
duration: 9,
loop: true
}
})
group.add(path)
group.add(car)
leafer.add(group)
一、动画
LeaferJS 这次提供了丰富的动画功能,支持延时、循环和 seek。你可以用它制作过渡动画、摇摆动画、关键帧动画、路径动画和滚动动画。支持以 animation、transition、animate() 方法、Animate 实例 等各种方式创建你所喜欢的动画。
1.入场和出场动画
可以用来直接开发页面过渡效果,以及元素的移入移出效果。
import { Group, Leafer, Frame } from 'leafer-ui'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window })
const page1 = new Frame({
x: 300,
y: 100,
width: 150,
height: 100,
fill: '#FEB027',
animation: { // 入场动画
keyframes: [{ opacity: 0, offsetX: -150 }, { opacity: 1, offsetX: 0 }],
duration: 0.8
},
animationOut: { // 出场动画
style: { opacity: 0, offsetX: 150 },
duration: 0.8
}
})
const page2 = page1.clone({ fill: '#32cd79' }) // 克隆 page 并重新设置fill
const group = new Group({ children: [page1] })
leafer.add(group)
// 切换页面, 自动执行入场、出场动画
setInterval(() => {
if (page1.parent) {
group.add(page2)
page1.remove()
} else {
group.add(page1)
page2.remove()
}
}, 2000)
2.摇摆动画
可以用来制作来回摇摆的动画效果。
import { Leafer, Rect } from 'leafer-ui'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window })
const rect = new Rect({
y: 100,
cornerRadius: 50,
fill: '#32cd79',
animation: {
style: { x: 500, cornerRadius: 0, fill: '#ffcd00' }, // style keyframe
duration: 1,
swing: true // 摇摆循环播放
}
})
leafer.add(rect)
3.关键帧动画
可以用来制作复杂的时间线动画,允许你精确控制每一帧的延迟、时长和缓动方式。
import { Leafer, Rect } from 'leafer-ui'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window })
const rect = new Rect({
x: 50,
y: 100,
cornerRadius: 50,
fill: '#32cd79',
around: 'center',
animation: {
keyframes: [
{ style: { x: 150, scaleX: 2, fill: '#ffcd00' }, duration: 0.5 }, // animate keyframe
{ style: { x: 50, scaleX: 1, fill: '#ffcd00' }, duration: 0.2 },
{ style: { x: 550, cornerRadius: 0, fill: '#ffcd00' }, delay: 0.1, easing: 'bounce-out' },
{ x: 50, rotation: -720, cornerRadius: 50 } // style keyframe
],
duration: 3, // 自动分配剩余的时长给未设置 duration 的关键帧: (3 - 0.5 - 0.2 - 0.1) / 2
loop: true,
join: true // 加入动画前的元素状态作为 from 关键帧
}
})
leafer.add(rect)
4.路径动画
让元素沿着另一条路径运动,或进行自身描边,已基本开发完成,测试和完善后将单独发布插件,敬请关注!
import { Group, Leafer, Path, Rect } from 'leafer-ui'
import '@leafer-in/motion-path'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window })
const group = new Group()
const path = new Path({
x: 100,
y: 100,
scale: 0.2,
motionPath: true, // 设置为运动路径,该 Group 内的其他元素都可以沿此路径运动
stroke: 'black',
strokeWidth: 20,
animation: { // 沿 path 运动描边至 100%
style: { motion: { type: "percent", value: 1 } },
duration: 9,
loop: true
},
path: 'M945.344 586.304c-13.056-93.44-132.48-98.048-132.48-98.048 0-29.888-39.808-47.424-39.808-47.424L201.664 440.832c-36.736 0-42.112 51.264-42.112 51.264 7.68 288 181.44 382.976 181.44 382.976l299.456 0c42.88-31.36 101.888-122.56 101.888-122.56 9.216 3.072 72.768-0.832 97.984-6.144C865.6 740.992 958.336 679.68 945.344 586.304zM365.568 825.28c-145.472-105.664-130.944-328.576-130.944-328.576l80.448 0c-44.416 126.4 43.648 285.696 55.872 307.904C383.232 826.816 365.568 825.28 365.568 825.28zM833.472 694.272c-37.568 22.272-65.152 7.68-65.152 7.68 39.04-54.4 42.112-159.296 42.112-159.296 6.848 2.304 12.288-26.048 61.312 23.744C920.768 616.128 871.04 672.064 833.472 694.272z M351.68 129.856c0 0-119.424 72.832-44.416 140.928 75.008 68.16 68.16 93.44 24.512 153.216 0 0 81.92-41.344 71.168-104.192s-89.6-94.208-72.768-137.792C347.136 138.304 351.68 129.856 351.68 129.856z M615.232 91.648c0 0-119.488 72.832-44.352 140.928 74.944 68.16 68.032 93.44 24.448 153.216 0 0 81.984-41.344 71.232-104.192-10.688-62.784-89.6-94.208-72.832-137.792C610.624 100.032 615.232 91.648 615.232 91.648z M491.136 64c0 0-74.304 6.144-88.128 78.144C389.248 214.144 435.968 240.96 471.936 276.992 507.904 312.96 492.608 380.352 452.032 427.904c0 0 72.768-25.344 89.6-94.976 16.832-69.76-17.344-94.272-52.8-134.784C453.312 157.504 456.64 83.968 491.136 64z',
})
const pen = new Rect({
width: 15,
height: 50,
cornerRadius: 10,
fill: '#FEB027',
around: 'bottom',
motionRotation: 45,
animation: { // 沿 path 运动至 100%
style: { motion: { type: "percent", value: 1 } },
duration: 9,
loop: true
}
})
group.add(path)
group.add(pen)
leafer.add(group)
5.滚动驱动动画
你可能见过一些网站在滚动页面时,页面元素会自动产生动画效果。这就是滚动驱动动画,它通过滚动条或元素移动来控制动画的播放,即将支持。
二、状态
可以像 CSS 一样为元素增加 hover 、 press 、 focus 、 selected 、 disabled 交互状态样式。还可以预设复杂多样的 state 状态,用于随时切换。父元素可通过设置 button 属性,使子元素自动同步交互状态。
1. 按钮状态
import { Leafer, Box } from 'leafer-ui'
import '@leafer-in/state'
const leafer = new Leafer({ view: window, fill: 'gray' })
const box = new Box({
x: 100,
y: 100,
fill: '#FEB027',
cornerRadius: 5,
button: true, // 标记为按钮,子元素 Text 将自动同步交互状态
hoverStyle: { fill: '#32cd79' }, // 鼠标hover状态
pressStyle: { fill: '#FF4B4B' }, // 鼠标按下状态
children: [{
tag: 'Text',
text: 'Button',
fontSize: 16,
padding: [10, 20],
fill: 'black',
hoverStyle: { fill: 'white' }, // 鼠标在 button 上hover的状态
pressStyle: { fontWeight: 'bold' } // 鼠标在 button 上按下的状态
}]
})
leafer.add(box)
2. 点击切换自定义状态
import { Leafer, Rect } from 'leafer-ui'
import '@leafer-in/state'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window })
const rect = new Rect({
width: 100,
height: 100,
fill: '#32cd79',
cornerRadius: 30,
origin: 'center',
states: { // 自定义状态列表
color: { fill: '#FEB027' },
rotate: { animation: { keyframes: [{ rotation: 45 }, { rotation: 135, scale: 1.2 }], duration: 1, swing: true } }
},
state: 'color', // 设置状态
transition: 1
})
leafer.add(rect)
rect.on('click', () => { // 点击切换状态
rect.state = rect.state === 'color' ? 'rotate' : 'color'
})
三、过渡
类似于 CSS 的过渡功能,但 LeaferJS 的过渡仅针对状态变化,不会在单独修改属性时触发过渡(提供更好的控制)。你可以分别设置进入和退出状态时的过渡动画。默认情况下,LeaferJS 自动开启了 state、hover 、 press 、 focus 、 selected 、 disabled 状态切换的过渡效果。
1.按钮交互过渡效果
切换不同交互状态时,流畅过渡样式,让交互体验更丝滑。
import { Leafer, Box } from 'leafer-ui'
import '@leafer-in/state'
import '@leafer-in/animate'
const leafer = new Leafer({ view: window, fill: 'gray' })
const box = new Box({
x: 100,
y: 100,
fill: '#32cd79',
cornerRadius: 5,
origin: 'center', // 从中心缩放
button: true, // 标记为按钮,子元素 Text 将自动同步交互状态
hoverStyle: { // 鼠标hover状态
fill: '#FF4B4B',
scale: 1.5,
cornerRadius: 20,
},
pressStyle: { // 鼠标按下状态
fill: '#FEB027',
scale: 1.1,
transitionOut: 'bounce-out' // 退出状态时的过渡方式
},
children: [{
tag: 'Text',
text: 'Button',
fontSize: 16,
fontWeight: 'bold',
padding: [10, 20],
fill: 'rgba(0,0,0,0.5)',
hoverStyle: { fill: 'black' } // 鼠标 hover 到 button 上的状态
}]
})
leafer.add(box)
2. 复杂渐变过渡
实色到渐变色的过渡,及不同渐变类型的过渡。已基本开发完成,测试和完善后将单独发布插件,敬请关注!
import { Leafer, Rect } from 'leafer-ui'
import '@leafer-in/state'
import '@leafer-in/animate'
import '@leafer-in/transition'
const leafer = new Leafer({ view: window })
const rect = new Rect({
x: 100,
y: 100,
width: 100,
height: 100,
cornerRadius: 30,
fill: {
type: 'linear',
stops: ['#FEB027', '#79CB4D']
},
hoverStyle: {
fill: {
type: 'radial',
from: 'top',
stops: ['#FF4B4B', '#FEB027']
}
},
transition: 1
})
leafer.add(rect)
四、游戏
之前做游戏时,最缺的就是一个能够自动播放动作帧的精灵元素,用于切换游戏动作。其他功能都可以结合第三方库来实现,如物理引擎推荐使用 Matter.js ,它易于上手、功能全面,可以模拟各种物理效果。
Robot 元素
Robot 元素类似于游戏中的精灵元素,集成了帧播放和动作预设功能,可以快速制作出具有行走和攻击动作的游戏角色。
首先,你需要提供一张包含所有游戏动作的雪碧图,这些动作将会被自动编号,如下所示:
通过加载和解析雪碧图,你将得到一个可以自由切换动作的游戏元素。
import { Leafer, KeyEvent } from 'leafer-ui'
import { Robot } from '@leafer-in/robot'
const leafer = new Leafer({ view: window })
const robot = new Robot({
robot: { url: '/image/arrows.png', size: { width: 100, height: 100 }, total: 20 },
actions: { // 预设游戏动作(通过动作帧)
up: 0, // 静止向上的箭头( 编号为0的动作帧)
right: 5,
down: 10,
left: 15,
arrowUp: [0, 1, 2, 3, 4], // 动态向上的箭头(循环播放编号为 1-4 的动作帧)
arrowRight: [5, 6, 7, 8, 9],
arrowDown: [10, 11, 12, 13, 14],
arrowLeft: [15, 16, 17, 18, 19]
},
action: 'right' // 设置动作:静止向右的箭头
})
leafer.add(robot)
// 监听方向键进行交互
let speed = 5
leafer.on(KeyEvent.DOWN, (e: KeyEvent) => {
speed++
switch (e.code) { // 动态的方向箭头
case 'ArrowUp':
robot.action = 'arrowUp'
robot.y -= speed
break
case 'ArrowDown':
robot.action = 'arrowDown'
robot.y += speed
break
case 'ArrowLeft':
robot.action = 'arrowLeft'
robot.x -= speed
break
case 'ArrowRight':
robot.action = 'arrowRight'
robot.x += speed
break
}
})
leafer.on(KeyEvent.UP, (e: KeyEvent) => {
speed = 5
switch (e.code) { // 静态的方向箭头
case 'ArrowUp':
robot.action = 'up'
break
case 'ArrowDown':
robot.action = 'down'
break
case 'ArrowLeft':
robot.action = 'left'
break
case 'ArrowRight':
robot.action = 'right'
break
}
})
运行示例代码
上面的示例代码可以通过我们与 Cloud Studio 合作提供的 在线 Playground 环境 直接运行。
LeaferJS 正在被更多的人看见
对于心怀“游戏梦”的开发者来说, LeaferJS 绝对是一个能让你轻松上手、实现游戏梦想的利器。
图中是 Cloud Studio 的产品经理 Megan Zhang 在 腾讯全球数字生态大会上推荐 LeaferJS 。
还在开发图形编辑应用的用户也不用担心,LeaferJS 现阶段依然专注于绘图、交互和图形编辑场景。我们引入游戏场景,只是希望让 LeaferJS 被更多有需要的人看到,以充分发挥它的价值。
我们将会继续推动我们的愿景,帮助更多用户创造出强大、实用的生产力工具。
总结
在这一期更新中,LeaferJS 为你带来了全新的游戏、动画、状态和过渡功能,助你实现那些年少时的游戏梦想。我们引入了丰富的动画效果,如摇摆动画、关键帧动画和路径动画(即将上线),以及实用的过渡功能和灵活的游戏元素,让你能够轻松制作出动感十足的游戏角色和页面效果。
这些功能不仅让你的开发工作变得更加高效,也让你能够创造出更具视觉冲击力的作品。接下来,更多令人兴奋的社区游戏作品和教程将会陆续出现,我也会参与其中,敬请关注,我们下期再见~