diff --git a/docs/pixi-version.json b/docs/pixi-version.json index 5e1f9c3b2..77b25fb55 100644 --- a/docs/pixi-version.json +++ b/docs/pixi-version.json @@ -1,10 +1,10 @@ { "versionLabel": "prerelease-v8", - "version": "8.0.0-rc", - "releaseNotes": "https://github.com/pixijs/pixijs/releases/tag/v8.0.0-rc", - "build": "https://pixijs.download/v8.0.0-rc/pixi.min.js", - "docs": "https://pixijs.download/v8.0.0-rc/docs/index.html", - "npm": "8.0.0-rc", + "version": "8.0.0-rc.1", + "releaseNotes": "https://github.com/pixijs/pixijs/releases/tag/v8.0.0-rc.1", + "build": "https://pixijs.download/v8.0.0-rc.1/pixi.min.js", + "docs": "https://pixijs.download/v8.0.0-rc.1/docs/index.html", + "npm": "8.0.0-rc.1", "prerelease": true, "latest": false } diff --git a/pixi-versions.json b/pixi-versions.json index b3ee5f1ac..26e50f439 100644 --- a/pixi-versions.json +++ b/pixi-versions.json @@ -12,11 +12,11 @@ }, { "versionLabel": "prerelease-v8", - "version": "8.0.0-rc", - "releaseNotes": "https://github.com/pixijs/pixijs/releases/tag/v8.0.0-rc", - "build": "https://pixijs.download/v8.0.0-rc/pixi.min.js", - "docs": "https://pixijs.download/v8.0.0-rc/docs/index.html", - "npm": "8.0.0-rc", + "version": "8.0.0-rc.1", + "releaseNotes": "https://github.com/pixijs/pixijs/releases/tag/v8.0.0-rc.1", + "build": "https://pixijs.download/v8.0.0-rc.1/pixi.min.js", + "docs": "https://pixijs.download/v8.0.0-rc.1/docs/index.html", + "npm": "8.0.0-rc.1", "prerelease": true, "latest": false, "isCurrent": true diff --git a/src/examples/index.ts b/src/examples/index.ts index ca4f2e393..876142d93 100644 --- a/src/examples/index.ts +++ b/src/examples/index.ts @@ -1,6 +1,6 @@ import type { OptionGroup } from '../components/Select'; import v7x from './v7.3.2/index'; -import v8x from './v8.0.0-rc/index'; +import v8x from './v8.0.0-rc.1/index'; export type ExampleDataEntry = { name: string; @@ -24,7 +24,7 @@ export type ExamplesDataType = Record; // TODO: Use await import to dynamically load versioned content on demand instead? const versions: Record = { '7.3.2': v7x, - '8.0.0-rc': v8x, + '8.0.0-rc.1': v8x, }; export function getExampleEntry(version: string, pathString: string): ExampleSourceEntry | undefined diff --git a/src/examples/v8.0.0-rc.1/advanced/collisionDetection.js b/src/examples/v8.0.0-rc.1/advanced/collisionDetection.js new file mode 100644 index 000000000..6e02def44 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/collisionDetection.js @@ -0,0 +1,194 @@ +import { Application, Assets, Point, Sprite, Texture } from 'pixi.js'; + +// Based somewhat on this article by Spicy Yoghurt +// URL for further reading: https://spicyyoghurt.com/tutorials/html5-javascript-game-development/collision-detection-physics +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#111', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Options for how objects interact + // How fast the red square moves + const movementSpeed = 0.05; + + // Strength of the impulse push between two objects + const impulsePower = 5; + + // Test For Hit + // A basic AABB check between two different squares + function testForAABB(object1, object2) + { + const bounds1 = object1.getBounds(); + const bounds2 = object2.getBounds(); + + return ( + bounds1.x < bounds2.x + bounds2.width + && bounds1.x + bounds1.width > bounds2.x + && bounds1.y < bounds2.y + bounds2.height + && bounds1.y + bounds1.height > bounds2.y + ); + } + + // Calculates the results of a collision, allowing us to give an impulse that + // shoves objects apart + function collisionResponse(object1, object2) + { + if (!object1 || !object2) + { + return new Point(0); + } + + const vCollision = new Point(object2.x - object1.x, object2.y - object1.y); + + const distance = Math.sqrt( + (object2.x - object1.x) * (object2.x - object1.x) + (object2.y - object1.y) * (object2.y - object1.y), + ); + + const vCollisionNorm = new Point(vCollision.x / distance, vCollision.y / distance); + + const vRelativeVelocity = new Point( + object1.acceleration.x - object2.acceleration.x, + object1.acceleration.y - object2.acceleration.y, + ); + + const speed = vRelativeVelocity.x * vCollisionNorm.x + vRelativeVelocity.y * vCollisionNorm.y; + + const impulse = (impulsePower * speed) / (object1.mass + object2.mass); + + return new Point(impulse * vCollisionNorm.x, impulse * vCollisionNorm.y); + } + + // Calculate the distance between two given points + function distanceBetweenTwoPoints(p1, p2) + { + const a = p1.x - p2.x; + const b = p1.y - p2.y; + + return Math.hypot(a, b); + } + + // The green square we will knock about + const greenSquare = new Sprite(Texture.WHITE); + + greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); + greenSquare.width = 100; + greenSquare.height = 100; + greenSquare.tint = 0x00ff00; + greenSquare.acceleration = new Point(0); + greenSquare.mass = 3; + + // The square you move around + const redSquare = new Sprite(Texture.WHITE); + + redSquare.position.set(0, 0); + redSquare.width = 100; + redSquare.height = 100; + redSquare.tint = 0xff0000; + redSquare.acceleration = new Point(0); + redSquare.mass = 1; + + const mouseCoords = { x: 0, y: 0 }; + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage.on('mousemove', (event) => + { + mouseCoords.x = event.global.x; + mouseCoords.y = event.global.y; + }); + + // Listen for animate update + app.ticker.add((time) => + { + const delta = time.deltaTime; + + // Applied deacceleration for both squares, done by reducing the + // acceleration by 0.01% of the acceleration every loop + redSquare.acceleration.set(redSquare.acceleration.x * 0.99, redSquare.acceleration.y * 0.99); + greenSquare.acceleration.set(greenSquare.acceleration.x * 0.99, greenSquare.acceleration.y * 0.99); + + // Check whether the green square ever moves off the screen + // If so, reverse acceleration in that direction + if (greenSquare.x < 0 || greenSquare.x > app.screen.width - 100) + { + greenSquare.acceleration.x = -greenSquare.acceleration.x; + } + + if (greenSquare.y < 0 || greenSquare.y > app.screen.height - 100) + { + greenSquare.acceleration.y = -greenSquare.acceleration.y; + } + + // If the green square pops out of the cordon, it pops back into the + // middle + if ( + greenSquare.x < -30 + || greenSquare.x > app.screen.width + 30 + || greenSquare.y < -30 + || greenSquare.y > app.screen.height + 30 + ) + { + greenSquare.position.set((app.screen.width - 100) / 2, (app.screen.height - 100) / 2); + } + + // If the mouse is off screen, then don't update any further + if ( + app.screen.width > mouseCoords.x + || mouseCoords.x > 0 + || app.screen.height > mouseCoords.y + || mouseCoords.y > 0 + ) + { + // Get the red square's center point + const redSquareCenterPosition = new Point( + redSquare.x + redSquare.width * 0.5, + redSquare.y + redSquare.height * 0.5, + ); + + // Calculate the direction vector between the mouse pointer and + // the red square + const toMouseDirection = new Point( + mouseCoords.x - redSquareCenterPosition.x, + mouseCoords.y - redSquareCenterPosition.y, + ); + + // Use the above to figure out the angle that direction has + const angleToMouse = Math.atan2(toMouseDirection.y, toMouseDirection.x); + + // Figure out the speed the square should be travelling by, as a + // function of how far away from the mouse pointer the red square is + const distMouseRedSquare = distanceBetweenTwoPoints(mouseCoords, redSquareCenterPosition); + const redSpeed = distMouseRedSquare * movementSpeed; + + // Calculate the acceleration of the red square + redSquare.acceleration.set(Math.cos(angleToMouse) * redSpeed, Math.sin(angleToMouse) * redSpeed); + } + + // If the two squares are colliding + if (testForAABB(greenSquare, redSquare)) + { + // Calculate the changes in acceleration that should be made between + // each square as a result of the collision + const collisionPush = collisionResponse(greenSquare, redSquare); + // Set the changes in acceleration for both squares + + redSquare.acceleration.set(collisionPush.x * greenSquare.mass, collisionPush.y * greenSquare.mass); + greenSquare.acceleration.set(-(collisionPush.x * redSquare.mass), -(collisionPush.y * redSquare.mass)); + } + + greenSquare.x += greenSquare.acceleration.x * delta; + greenSquare.y += greenSquare.acceleration.y * delta; + + redSquare.x += redSquare.acceleration.x * delta; + redSquare.y += redSquare.acceleration.y * delta; + }); + + // Add to stage + app.stage.addChild(redSquare, greenSquare); +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/mouseTrail.js b/src/examples/v8.0.0-rc.1/advanced/mouseTrail.js new file mode 100644 index 000000000..7c3baa059 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/mouseTrail.js @@ -0,0 +1,111 @@ +import { Application, Assets, Point, Sprite, MeshRope } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture for rope. + const trailTexture = await Assets.load('https://pixijs.com/assets/trail.png'); + + const historyX = []; + const historyY = []; + // historySize determines how long the trail will be. + const historySize = 20; + // ropeSize determines how smooth the trail will be. + const ropeSize = 100; + const points = []; + + // Create history array. + for (let i = 0; i < historySize; i++) + { + historyX.push(0); + + historyY.push(0); + } + // Create rope points. + for (let i = 0; i < ropeSize; i++) + { + points.push(new Point(0, 0)); + } + + // Create the rope + const rope = new MeshRope({ texture: trailTexture, points }); + + // Set the blendmode + rope.blendmode = 'add'; + + app.stage.addChild(rope); + + let mouseposition = null; + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage.on('mousemove', (event) => + { + mouseposition = mouseposition || { x: 0, y: 0 }; + mouseposition.x = event.global.x; + mouseposition.y = event.global.y; + }); + + // Listen for animate update + app.ticker.add(() => + { + if (!mouseposition) return; + + // Update the mouse values to history + historyX.pop(); + historyX.unshift(mouseposition.x); + historyY.pop(); + historyY.unshift(mouseposition.y); + // Update the points to correspond with history. + for (let i = 0; i < ropeSize; i++) + { + const p = points[i]; + + // Smooth the curve with cubic interpolation to prevent sharp edges. + const ix = cubicInterpolation(historyX, (i / ropeSize) * historySize); + const iy = cubicInterpolation(historyY, (i / ropeSize) * historySize); + + p.x = ix; + p.y = iy; + } + }); + + /** + * Cubic interpolation based on https://github.com/osuushi/Smooth.js + */ + function clipInput(k, arr) + { + if (k < 0) k = 0; + if (k > arr.length - 1) k = arr.length - 1; + + return arr[k]; + } + + function getTangent(k, factor, array) + { + return (factor * (clipInput(k + 1, array) - clipInput(k - 1, array))) / 2; + } + + function cubicInterpolation(array, t, tangentFactor) + { + if (tangentFactor === null) tangentFactor = 1; + + const k = Math.floor(t); + const m = [getTangent(k, tangentFactor, array), getTangent(k + 1, tangentFactor, array)]; + const p = [clipInput(k, array), clipInput(k + 1, array)]; + + t -= k; + const t2 = t * t; + const t3 = t * t2; + + return (2 * t3 - 3 * t2 + 1) * p[0] + (t3 - 2 * t2 + t) * m[0] + (-2 * t3 + 3 * t2) * p[1] + (t3 - t2) * m[1]; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/scratchCard.js b/src/examples/v8.0.0-rc.1/advanced/scratchCard.js new file mode 100644 index 000000000..991fcbb66 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/scratchCard.js @@ -0,0 +1,86 @@ +import { Application, Assets, Graphics, Sprite, RenderTexture, Point } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // prepare circle texture, that will be our brush + const brush = new Graphics().circle(0, 0, 50).fill({ color: 0xffffff }); + + // Create a line that will interpolate the drawn points + const line = new Graphics(); + + // Load the textures + await Assets.load(['https://pixijs.com/assets/bg_grass.jpg', 'https://pixijs.com/assets/bg_rotate.jpg']); + + const { width, height } = app.screen; + const stageSize = { width, height }; + + const background = Object.assign(Sprite.from('https://pixijs.com/assets/bg_grass.jpg'), stageSize); + const imageToReveal = Object.assign(Sprite.from('https://pixijs.com/assets/bg_rotate.jpg'), stageSize); + const renderTexture = RenderTexture.create(stageSize); + const renderTextureSprite = new Sprite(renderTexture); + + imageToReveal.mask = renderTextureSprite; + + app.stage.addChild(background, imageToReveal, renderTextureSprite); + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage + .on('pointerdown', pointerDown) + .on('pointerup', pointerUp) + .on('pointerupoutside', pointerUp) + .on('pointermove', pointerMove); + + let dragging = false; + let lastDrawnPoint = null; + + function pointerMove({ global: { x, y } }) + { + if (dragging) + { + brush.position.set(x, y); + app.renderer.render({ + container: brush, + target: renderTexture, + clear: false, + skipUpdateTransform: false, + }); + // Smooth out the drawing a little bit to make it look nicer + // this connects the previous drawn point to the current one + // using a line + if (lastDrawnPoint) + { + line.clear().moveTo(lastDrawnPoint.x, lastDrawnPoint.y).lineTo(x, y).stroke({ width: 100, color: 0xffffff }); + app.renderer.render({ + container: line, + target: renderTexture, + clear: false, + skipUpdateTransform: false, + }); + } + lastDrawnPoint = lastDrawnPoint || new Point(); + lastDrawnPoint.set(x, y); + } + } + + function pointerDown(event) + { + dragging = true; + pointerMove(event); + } + + function pointerUp(event) + { + dragging = false; + lastDrawnPoint = null; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/screenShot.js b/src/examples/v8.0.0-rc.1/advanced/screenShot.js new file mode 100644 index 000000000..3ca59ca59 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/screenShot.js @@ -0,0 +1,71 @@ +import { Application, Assets, Container, Sprite, Text, TextStyle } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#111', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create and add a container for the bunnies + const bunnyContainer = new Container(); + + // Take the screenshot and download it + async function takeScreenshot() + { + app.stop(); + const url = await app.renderer.extract.base64(bunnyContainer); + const a = document.createElement('a'); + + document.body.append(a); + a.download = 'screenshot'; + a.href = url; + a.click(); + a.remove(); + app.start(); + } + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage.on('pointerdown', takeScreenshot); + + for (let i = 0; i < 25; i++) + { + const bunny = new Sprite(texture); + + bunny.anchor.set(0.5); + bunny.x = (i % 5) * 40; + bunny.y = Math.floor(i / 5) * 40; + bunnyContainer.addChild(bunny); + } + + bunnyContainer.x = 400; + bunnyContainer.y = 300; + bunnyContainer.pivot.x = bunnyContainer.width / 2; + bunnyContainer.pivot.y = bunnyContainer.height / 2; + + // Animate the bunnies container + app.ticker.add((time) => + { + bunnyContainer.rotation += 0.01 * time.deltaTime; + }); + + const style = new TextStyle({ + fontFamily: 'Roboto', + fill: '#999', + }); + + const screenshotText = new Text({ text: 'Click To Take Screenshot', style }); + + screenshotText.x = Math.round((app.screen.width - screenshotText.width) / 2); + screenshotText.y = Math.round(screenshotText.height / 2); + + app.stage.addChild(screenshotText, bunnyContainer); +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/slots.js b/src/examples/v8.0.0-rc.1/advanced/slots.js new file mode 100644 index 000000000..b0fe45231 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/slots.js @@ -0,0 +1,262 @@ +import { + Application, + Assets, + Color, + Container, + Texture, + Sprite, + Graphics, + Text, + TextStyle, + BlurFilter, + FillGradient, +} from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/eggHead.png', + 'https://pixijs.com/assets/flowerTop.png', + 'https://pixijs.com/assets/helmlok.png', + 'https://pixijs.com/assets/skully.png', + ]); + + const REEL_WIDTH = 160; + const SYMBOL_SIZE = 150; + + // Create different slot symbols + const slotTextures = [ + Texture.from('https://pixijs.com/assets/eggHead.png'), + Texture.from('https://pixijs.com/assets/flowerTop.png'), + Texture.from('https://pixijs.com/assets/helmlok.png'), + Texture.from('https://pixijs.com/assets/skully.png'), + ]; + + // Build the reels + const reels = []; + const reelContainer = new Container(); + + for (let i = 0; i < 5; i++) + { + const rc = new Container(); + + rc.x = i * REEL_WIDTH; + reelContainer.addChild(rc); + + const reel = { + container: rc, + symbols: [], + position: 0, + previousPosition: 0, + blur: new BlurFilter(), + }; + + reel.blur.blurX = 0; + reel.blur.blurY = 0; + rc.filters = [reel.blur]; + + // Build the symbols + for (let j = 0; j < 4; j++) + { + const symbol = new Sprite(slotTextures[Math.floor(Math.random() * slotTextures.length)]); + // Scale the symbol to fit symbol area. + + symbol.y = j * SYMBOL_SIZE; + symbol.scale.x = symbol.scale.y = Math.min(SYMBOL_SIZE / symbol.width, SYMBOL_SIZE / symbol.height); + symbol.x = Math.round((SYMBOL_SIZE - symbol.width) / 2); + reel.symbols.push(symbol); + rc.addChild(symbol); + } + reels.push(reel); + } + app.stage.addChild(reelContainer); + + // Build top & bottom covers and position reelContainer + const margin = (app.screen.height - SYMBOL_SIZE * 3) / 2; + + reelContainer.y = margin; + reelContainer.x = Math.round(app.screen.width - REEL_WIDTH * 5); + const top = new Graphics().rect(0, 0, app.screen.width, margin).fill({ color: 0x0 }); + const bottom = new Graphics().rect(0, SYMBOL_SIZE * 3 + margin, app.screen.width, margin).fill({ color: 0x0 }); + + // Create gradient fill + const fill = new FillGradient(0, 0, 0, 36 * 1.7); + + const colors = [0xffffff, 0x00ff99].map((color) => Color.shared.setValue(color).toNumber()); + + colors.forEach((number, index) => + { + const ratio = index / colors.length; + + fill.addColorStop(ratio, number); + }); + + // Add play text + const style = new TextStyle({ + fontFamily: 'Arial', + fontSize: 36, + fontStyle: 'italic', + fontWeight: 'bold', + fill: { fill }, + stroke: { color: 0x4a1850, width: 5 }, + dropShadow: { + color: 0x000000, + angle: Math.PI / 6, + blur: 4, + distance: 6, + }, + wordWrap: true, + wordWrapWidth: 440, + }); + + const playText = new Text('Spin the wheels!', style); + + playText.x = Math.round((bottom.width - playText.width) / 2); + playText.y = app.screen.height - margin + Math.round((margin - playText.height) / 2); + bottom.addChild(playText); + + // Add header text + const headerText = new Text('PIXI MONSTER SLOTS!', style); + + headerText.x = Math.round((top.width - headerText.width) / 2); + headerText.y = Math.round((margin - headerText.height) / 2); + top.addChild(headerText); + + app.stage.addChild(top); + app.stage.addChild(bottom); + + // Set the interactivity. + bottom.eventMode = 'static'; + bottom.cursor = 'pointer'; + bottom.addListener('pointerdown', () => + { + startPlay(); + }); + + let running = false; + + // Function to start playing. + function startPlay() + { + if (running) return; + running = true; + + for (let i = 0; i < reels.length; i++) + { + const r = reels[i]; + const extra = Math.floor(Math.random() * 3); + const target = r.position + 10 + i * 5 + extra; + const time = 2500 + i * 600 + extra * 600; + + tweenTo(r, 'position', target, time, backout(0.5), null, i === reels.length - 1 ? reelsComplete : null); + } + } + + // Reels done handler. + function reelsComplete() + { + running = false; + } + + // Listen for animate update. + app.ticker.add(() => + { + // Update the slots. + for (let i = 0; i < reels.length; i++) + { + const r = reels[i]; + // Update blur filter y amount based on speed. + // This would be better if calculated with time in mind also. Now blur depends on frame rate. + + r.blur.blurY = (r.position - r.previousPosition) * 8; + r.previousPosition = r.position; + + // Update symbol positions on reel. + for (let j = 0; j < r.symbols.length; j++) + { + const s = r.symbols[j]; + const prevy = s.y; + + s.y = ((r.position + j) % r.symbols.length) * SYMBOL_SIZE - SYMBOL_SIZE; + if (s.y < 0 && prevy > SYMBOL_SIZE) + { + // Detect going over and swap a texture. + // This should in proper product be determined from some logical reel. + s.texture = slotTextures[Math.floor(Math.random() * slotTextures.length)]; + s.scale.x = s.scale.y = Math.min(SYMBOL_SIZE / s.texture.width, SYMBOL_SIZE / s.texture.height); + s.x = Math.round((SYMBOL_SIZE - s.width) / 2); + } + } + } + }); + + // Very simple tweening utility function. This should be replaced with a proper tweening library in a real product. + const tweening = []; + + function tweenTo(object, property, target, time, easing, onchange, oncomplete) + { + const tween = { + object, + property, + propertyBeginValue: object[property], + target, + easing, + time, + change: onchange, + complete: oncomplete, + start: Date.now(), + }; + + tweening.push(tween); + + return tween; + } + // Listen for animate update. + app.ticker.add(() => + { + const now = Date.now(); + const remove = []; + + for (let i = 0; i < tweening.length; i++) + { + const t = tweening[i]; + const phase = Math.min(1, (now - t.start) / t.time); + + t.object[t.property] = lerp(t.propertyBeginValue, t.target, t.easing(phase)); + if (t.change) t.change(t); + if (phase === 1) + { + t.object[t.property] = t.target; + if (t.complete) t.complete(t); + remove.push(t); + } + } + for (let i = 0; i < remove.length; i++) + { + tweening.splice(tweening.indexOf(remove[i]), 1); + } + }); + + // Basic lerp funtion. + function lerp(a1, a2, t) + { + return a1 * (1 - t) + a2 * t; + } + + // Backout function from tweenjs. + // https://github.com/CreateJS/TweenJS/blob/master/src/tweenjs/Ease.js + function backout(amount) + { + return (t) => --t * t * ((amount + 1) * t + amount) + 1; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/spinners.js b/src/examples/v8.0.0-rc.1/advanced/spinners.js new file mode 100644 index 000000000..a5daf54bc --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/spinners.js @@ -0,0 +1,422 @@ +import { Application, Assets, Container, Sprite, Point, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/bg_scene_rotate.jpg', + 'https://pixijs.com/assets/bg_rotate.jpg', + 'https://pixijs.com/assets/circle.png', + ]); + + /* --------------------------------------- + Spinner 1. Square with radial completion. + -------------------------------------- */ + const generateSpinner1 = (position) => + { + const container = new Container(); + + container.position = position; + app.stage.addChild(container); + + const base = Sprite.from('https://pixijs.com/assets/bg_scene_rotate.jpg'); + const size = 100; + + base.width = size; + base.height = size; + + const bottom = Sprite.from('https://pixijs.com/assets/bg_rotate.jpg'); + + bottom.width = size; + bottom.height = size; + + const mask = new Graphics(); + + mask.position.set(size / 2, size / 2); + base.mask = mask; + window.mask = mask; + + container.addChild(bottom); + container.addChild(base); + container.addChild(mask); + + let phase = 0; + + return (delta) => + { + // Update phase + phase += delta / 60; + phase %= Math.PI * 2; + + // Calculate target point. + const x = Math.cos(phase - Math.PI / 2) * size; + const y = Math.sin(phase - Math.PI / 2) * size; + + const segments = [ + [-size / 2, -size / 2, size / 2, -size / 2], // top segment + [size / 2, -size / 2, size / 2, size / 2], // right + [-size / 2, size / 2, size / 2, size / 2], // bottom + [-size / 2, -size / 2, -size / 2, size / 2], // left + ]; + + // Find the intersecting segment. + let intersection = null; + let winding = 0; + + for (let i = 0; i < segments.length; i++) + { + const segment = segments[i]; + const hit = intersect(0, 0, x, y, segment[0], segment[1], segment[2], segment[3]); + + if (hit) + { + intersection = hit; + if (i === 0) winding = hit.x > 0 ? 0 : 4; + else winding = i; + break; + } + } + + const corners = [ + size / 2, + -size / 2, // Top right + size / 2, + size / 2, // Bottom right + -size / 2, + size / 2, // Bottom left + -size / 2, + -size / 2, // Top left, + 0, + -size / 2, // End point + ]; + + // Redraw mask + mask.clear() + .moveTo(0, -size / 2) + .lineTo(0, 0) + .lineTo(intersection.x, intersection.y); + + // fill the corners + for (let i = winding; i < corners.length / 2; i++) + { + mask.lineTo(corners[i * 2], corners[i * 2 + 1]); + } + + mask.fill({ color: 0xff0000 }); + }; + }; + + /* ----------------------- + Spinner 2. Scaling balls. + ---------------------- */ + const generateSpinner2 = (position) => + { + const container = new Container(); + + container.position = position; + app.stage.addChild(container); + + const size = 100; + const ballAmount = 7; + const balls = []; + + for (let i = 0; i < ballAmount; i++) + { + const ball = Sprite.from('https://pixijs.com/assets/circle.png'); + + ball.anchor.set(0.5); + container.addChild(ball); + ball.position.set( + size / 2 + (Math.cos((i / ballAmount) * Math.PI * 2) * size) / 3, + size / 2 + (Math.sin((i / ballAmount) * Math.PI * 2) * size) / 3, + ); + balls.push(ball); + } + + let phase = 0; + + return (delta) => + { + // Update phase + phase += delta / 60; + phase %= Math.PI * 2; + + // Update ball scales + balls.forEach((b, i) => + { + const sin = Math.sin((i / ballAmount) * Math.PI - phase); + // Multiply sin with itself to get more steeper edge. + + b.scale.set(Math.abs(sin * sin * sin * 0.5) + 0.5); + }); + }; + }; + + /* --------------------- + Spinner 3. Radial mask. + -------------------- */ + const generateSpinner3 = (position) => + { + const container = new Container(); + + container.position = position; + app.stage.addChild(container); + + const base = Sprite.from('https://pixijs.com/assets/bg_scene_rotate.jpg'); + const size = 100; + + base.width = size; + base.height = size; + + const mask = new Graphics(); + + mask.position.set(size / 2, size / 2); + base.mask = mask; + window.mask = mask; + + container.addChild(base); + container.addChild(mask); + + let phase = 0; + + return (delta) => + { + // Update phase + phase += delta / 60; + phase %= Math.PI * 2; + + const angleStart = 0 - Math.PI / 2; + const angle = phase + angleStart; + const radius = 50; + + const x1 = Math.cos(angleStart) * radius; + const y1 = Math.sin(angleStart) * radius; + + // Redraw mask + mask.clear() + .moveTo(0, 0) + .lineTo(x1, y1) + .arc(0, 0, radius, angleStart, angle, false) + .lineTo(0, 0) + .fill({ color: 0xff0000 }); + }; + }; + + /* --------------------------------- + Spinner 4. Rounded rectangle edges. + ------------------------------- */ + const generateSpinner4 = (position) => + { + const container = new Container(); + + container.position = position; + app.stage.addChild(container); + + const size = 100; + const arcRadius = 15; + + const base = Sprite.from('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + base.width = size; + base.height = size; + + // For better performance having assets prerounded would be better than masking. + const roundingMask = new Graphics(); + + roundingMask.roundRect(0, 0, size, size, arcRadius).fill({ color: 0x0 }); + base.mask = roundingMask; + + // The edge could be replaced with image as well. + const lineSize = 5; + const edge = new Graphics(); + + edge.roundRect(0, 0, size, size, arcRadius).stroke({ width: lineSize, color: 0xff0000 }); + + // Mask in this example works basically the same way as in example 1. + // Except it is reversed and calculates the mask in straight lines in edges. + const mask = new Graphics(); + + mask.position.set(size / 2, size / 2); + edge.mask = mask; + + container.addChild(base); + container.addChild(roundingMask); + container.addChild(edge); + container.addChild(mask); + + let phase = 0; + + return (delta) => + { + // Update phase + phase += delta / 160; + phase %= Math.PI * 2; + + // Calculate target point. + const x = Math.cos(phase - Math.PI / 2) * size; + const y = Math.sin(phase - Math.PI / 2) * size; + // Line segments + const segments = [ + [-size / 2 + lineSize, -size / 2 + lineSize, size / 2 - lineSize, -size / 2 + lineSize], // top segment + [size / 2 - lineSize, -size / 2 + lineSize, size / 2 - lineSize, size / 2 - lineSize], // right + [-size / 2 + lineSize, size / 2 - lineSize, size / 2 - lineSize, size / 2 - lineSize], // bottom + [-size / 2 + lineSize, -size / 2 + lineSize, -size / 2 + lineSize, size / 2 - lineSize], // left + ]; + // To which dir should mask continue at each segment + let outDir = [ + [0, -1], + [1, 0], + [0, 1], + [-1, 0], + ]; + + // Find the intersecting segment. + let intersection = null; + let winding = 0; + // What direction should the line continue after hit has been found before hitting the line size + + for (let i = 0; i < segments.length; i++) + { + const segment = segments[i]; + const hit = intersect(0, 0, x, y, segment[0], segment[1], segment[2], segment[3]); + + if (hit) + { + intersection = hit; + if (i === 0) winding = hit.x < 0 ? 0 : 4; + else winding = 4 - i; + outDir = outDir[i]; + break; + } + } + + const corners = [ + -size / 2 - lineSize, + -size / 2 - lineSize, // Top left, + -size / 2 - lineSize, + size / 2 + lineSize, // Bottom left + size / 2 + lineSize, + size / 2 + lineSize, // Bottom right + size / 2 + lineSize, + -size / 2 - lineSize, // Top right + ]; + + // Redraw mask + mask.clear() + .moveTo(0, 0) + .moveTo(0, -size / 2 - lineSize); + + // fill the corners + for (let i = 0; i < winding; i++) + { + mask.lineTo(corners[i * 2], corners[i * 2 + 1]); + } + + mask.lineTo(intersection.x + outDir[0] * lineSize * 2, intersection.y + outDir[1] * lineSize * 2) + .lineTo(intersection.x, intersection.y) + .lineTo(0, 0) + .fill({ color: 0xff0000 }); + }; + }; + + /* --------------------- + Spinner 5. Rounded rectangle fixed length spinner by jonlepage + -------------------- */ + const generateSpinner5 = (position) => + { + const container = new Container(); + + container.position = position; + app.stage.addChild(container); + + const halfCircle = new Graphics().arc(0, 0, 100, 0, Math.PI).fill({ color: 0xff0000 }); + + halfCircle.position.set(50, 50); + + const rectangle = new Graphics().roundRect(0, 0, 100, 100, 16).stroke({ width: 2, color: 0xffffff }); + + rectangle.mask = halfCircle; + + container.addChild(rectangle); + container.addChild(halfCircle); + + let phase = 0; + + return (delta) => + { + // Update phase + phase += delta / 6; + phase %= Math.PI * 2; + + halfCircle.rotation = phase; + }; + }; + + const onTick = [ + generateSpinner1(new Point(50, 50)), + generateSpinner2(new Point(160, 50)), + generateSpinner3(new Point(270, 50)), + generateSpinner4(new Point(380, 50)), + generateSpinner5(new Point(490, 50)), + ]; + + // Listen for animate update + app.ticker.add((time) => + { + // Call tick handling for each spinner. + onTick.forEach((cb) => + { + cb(time.deltaTime); + }); + }); + + /** + * Helper functions + + line intercept math by Paul Bourke http://paulbourke.net/geometry/pointlineplane/ + Determine the intersection point of two line segments + Return FALSE if the lines don't intersect + + Code modified from original to match pixi examples linting rules. + */ + function intersect(x1, y1, x2, y2, x3, y3, x4, y4) + { + // Check if none of the lines are of length 0 + if ((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) + { + return false; + } + + const denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + + // Lines are parallel + if (denominator === 0) + { + return false; + } + + const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator; + const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator; + + // is the intersection along the segments + if (ua < 0 || ua > 1 || ub < 0 || ub > 1) + { + return false; + } + + // Return a object with the x and y coordinates of the intersection + const x = x1 + ua * (x2 - x1); + const y = y1 + ua * (y2 - y1); + + return { x, y }; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/advanced/starWarp.js b/src/examples/v8.0.0-rc.1/advanced/starWarp.js new file mode 100644 index 000000000..6ac7c7ad7 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/advanced/starWarp.js @@ -0,0 +1,97 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the star texture + const starTexture = await Assets.load('https://pixijs.com/assets/star.png'); + + const starAmount = 1000; + let cameraZ = 0; + const fov = 20; + const baseSpeed = 0.025; + let speed = 0; + let warpSpeed = 0; + const starStretch = 5; + const starBaseSize = 0.05; + + // Create the stars + const stars = []; + + for (let i = 0; i < starAmount; i++) + { + const star = { + sprite: new Sprite(starTexture), + z: 0, + x: 0, + y: 0, + }; + + star.sprite.anchor.x = 0.5; + star.sprite.anchor.y = 0.7; + randomizeStar(star, true); + app.stage.addChild(star.sprite); + stars.push(star); + } + + function randomizeStar(star, initial) + { + star.z = initial ? Math.random() * 2000 : cameraZ + Math.random() * 1000 + 2000; + + // Calculate star positions with radial random coordinate so no star hits the camera. + const deg = Math.random() * Math.PI * 2; + const distance = Math.random() * 50 + 1; + + star.x = Math.cos(deg) * distance; + star.y = Math.sin(deg) * distance; + } + + // Change flight speed every 5 seconds + setInterval(() => + { + warpSpeed = warpSpeed > 0 ? 0 : 1; + }, 5000); + + // Listen for animate update + app.ticker.add((time) => + { + // Simple easing. This should be changed to proper easing function when used for real. + speed += (warpSpeed - speed) / 20; + cameraZ += time.deltaTime * 10 * (speed + baseSpeed); + for (let i = 0; i < starAmount; i++) + { + const star = stars[i]; + + if (star.z < cameraZ) randomizeStar(star); + + // Map star 3d position to 2d with really simple projection + const z = star.z - cameraZ; + + star.sprite.x = star.x * (fov / z) * app.renderer.screen.width + app.renderer.screen.width / 2; + star.sprite.y = star.y * (fov / z) * app.renderer.screen.width + app.renderer.screen.height / 2; + + // Calculate star scale & rotation. + const dxCenter = star.sprite.x - app.renderer.screen.width / 2; + const dyCenter = star.sprite.y - app.renderer.screen.height / 2; + const distanceCenter = Math.sqrt(dxCenter * dxCenter + dyCenter * dyCenter); + const distanceScale = Math.max(0, (2000 - z) / 2000); + + star.sprite.scale.x = distanceScale * starBaseSize; + // Star is looking towards center so that y axis is towards center. + // Scale the star depending on how fast we are moving, what the stretchfactor is + // and depending on how far away it is from the center. + star.sprite.scale.y + = distanceScale * starBaseSize + + (distanceScale * speed * starStretch * distanceCenter) / app.renderer.screen.width; + star.sprite.rotation = Math.atan2(dyCenter, dxCenter) + Math.PI / 2; + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/assets/async.js b/src/examples/v8.0.0-rc.1/assets/async.js new file mode 100644 index 000000000..1696dd5c4 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/assets/async.js @@ -0,0 +1,31 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +// Await can only be used inside an async function +async function init() +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create a new Sprite from the awaited loaded Texture + const bunny = Sprite.from(texture); + + // Center the sprite's anchor point + bunny.anchor.set(0.5); + + // Move the sprite to the center of the screen + bunny.x = app.screen.width / 2; + bunny.y = app.screen.height / 2; + + app.stage.addChild(bunny); +} + +// Call that async function +init(); diff --git a/src/examples/v8.0.0-rc.1/assets/background.js b/src/examples/v8.0.0-rc.1/assets/background.js new file mode 100644 index 000000000..f91faad77 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/assets/background.js @@ -0,0 +1,43 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +// Create a new application +const app = new Application(); + +// Initialize the application +app.init({ background: '#1099bb', resizeTo: window }).then(() => +{ + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Add the assets to load + Assets.add({ alias: 'flowerTop', src: 'https://pixijs.com/assets/flowerTop.png' }); + Assets.add({ alias: 'eggHead', src: 'https://pixijs.com/assets/eggHead.png' }); + + // Allow the assets to load in the background + Assets.backgroundLoad(['flowerTop', 'eggHead']); + + // If the background load hasn't loaded this asset yet, calling load forces this asset to load now. + Assets.load('eggHead').then((texture) => + { + // Auxiliar flag for toggling the texture + let isEggHead = true; + + // Create a new Sprite from the resolved loaded texture + const character = new Sprite(texture); + + character.anchor.set(0.5); + character.x = app.screen.width / 2; + character.y = app.screen.height / 2; + character.eventMode = 'static'; + character.cursor = 'pointer'; + + app.stage.addChild(character); + + character.on('pointertap', async () => + { + isEggHead = !isEggHead; + // These promise are already resolved in the cache. + character.texture = await Assets.load(isEggHead ? 'eggHead' : 'flowerTop'); + }); + }); +}); diff --git a/src/examples/v8.0.0-rc.1/assets/bundle.js b/src/examples/v8.0.0-rc.1/assets/bundle.js new file mode 100644 index 000000000..f1233ddbb --- /dev/null +++ b/src/examples/v8.0.0-rc.1/assets/bundle.js @@ -0,0 +1,96 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +// Create a new application +const app = new Application(); + +async function init() +{ + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Manifest example + const manifestExample = { + bundles: [ + { + name: 'load-screen', + assets: [ + { + alias: 'flowerTop', + src: 'https://pixijs.com/assets/flowerTop.png', + }, + ], + }, + { + name: 'game-screen', + assets: [ + { + alias: 'eggHead', + src: 'https://pixijs.com/assets/eggHead.png', + }, + ], + }, + ], + }; + + await Assets.init({ manifest: manifestExample }); + + // Bundles can be loaded in the background too! + Assets.backgroundLoadBundle(['load-screen', 'game-screen']); + + makeLoadScreen(); +} + +async function makeLoadScreen() +{ + // Get the assets from the load screen bundle. + // If the bundle was already downloaded the promise resolves instantly! + const loadScreenAssets = await Assets.loadBundle('load-screen'); + + // Create a new Sprite from the resolved loaded texture + const goNext = new Sprite(loadScreenAssets.flowerTop); + + goNext.anchor.set(0.5); + goNext.x = app.screen.width / 2; + goNext.y = app.screen.height / 2; + app.stage.addChild(goNext); + + goNext.eventMode = 'static'; + goNext.cursor = 'pointer'; + + goNext.on('pointertap', async () => + { + goNext.destroy(); + makeGameScreen(); + }); +} + +async function makeGameScreen() +{ + // Wait here until you get the assets + // If the user spends enough time in the load screen by the time they reach the game screen + // the assets are completely loaded and the promise resolves instantly! + const loadScreenAssets = await Assets.loadBundle('game-screen'); + + // Create a new Sprite from the resolved loaded texture + const goBack = new Sprite(loadScreenAssets.eggHead); + + goBack.anchor.set(0.5); + goBack.x = app.screen.width / 2; + goBack.y = app.screen.height / 2; + app.stage.addChild(goBack); + + goBack.eventMode = 'static'; + goBack.cursor = 'pointer'; + + goBack.on('pointertap', async () => + { + goBack.destroy(); + // The user can go back and the files are already downloaded + makeLoadScreen(); + }); +} + +init(); diff --git a/src/examples/v8.0.0-rc.1/assets/multiple.js b/src/examples/v8.0.0-rc.1/assets/multiple.js new file mode 100644 index 000000000..41358878a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/assets/multiple.js @@ -0,0 +1,37 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +// Create a new application +const app = new Application(); + +// Initialize the application +app.init({ background: '#1099bb', resizeTo: window }).then(() => +{ + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Add the assets to load + Assets.add({ alias: 'flowerTop', src: 'https://pixijs.com/assets/flowerTop.png' }); + Assets.add({ alias: 'eggHead', src: 'https://pixijs.com/assets/eggHead.png' }); + + // Load the assets and get a resolved promise once both are loaded + const texturesPromise = Assets.load(['flowerTop', 'eggHead']); // => Promise<{flowerTop: Texture, eggHead: Texture}> + + // When the promise resolves, we have the texture! + texturesPromise.then((textures) => + { + // Create a new Sprite from the resolved loaded Textures + const flower = Sprite.from(textures.flowerTop); + + flower.anchor.set(0.5); + flower.x = app.screen.width * 0.25; + flower.y = app.screen.height / 2; + app.stage.addChild(flower); + + const egg = Sprite.from(textures.eggHead); + + egg.anchor.set(0.5); + egg.x = app.screen.width * 0.75; + egg.y = app.screen.height / 2; + app.stage.addChild(egg); + }); +}); diff --git a/src/examples/v8.0.0-rc.1/assets/promise.js b/src/examples/v8.0.0-rc.1/assets/promise.js new file mode 100644 index 000000000..edd3d309e --- /dev/null +++ b/src/examples/v8.0.0-rc.1/assets/promise.js @@ -0,0 +1,32 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Start loading right away and create a promise + const texturePromise = Assets.load('https://pixijs.com/assets/bunny.png'); + + // When the promise resolves, we have the texture! + texturePromise.then((resolvedTexture) => + { + // create a new Sprite from the resolved loaded Texture + const bunny = Sprite.from(resolvedTexture); + + // center the sprite's anchor point + bunny.anchor.set(0.5); + + // move the sprite to the center of the screen + bunny.x = app.screen.width / 2; + bunny.y = app.screen.height / 2; + + app.stage.addChild(bunny); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/blendModes.js b/src/examples/v8.0.0-rc.1/basic/blendModes.js new file mode 100644 index 000000000..3dc03bac5 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/blendModes.js @@ -0,0 +1,104 @@ +import { Application, Assets, Sprite, Rectangle } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + const bgTexture = await Assets.load('https://pixijs.com/assets/bg_rotate.jpg'); + const alienTexture = await Assets.load('https://pixijs.com/assets/flowerTop.png'); + + // Create a new background sprite + const background = new Sprite(bgTexture); + + background.width = app.screen.width; + background.height = app.screen.height; + app.stage.addChild(background); + + // Create an array to store references to the dudes + const dudeArray = []; + + const totaldudes = 20; + + for (let i = 0; i < totaldudes; i++) + { + // Create a new alien Sprite + const dude = new Sprite(alienTexture); + + dude.anchor.set(0.5); + + // Set a random scale for the dude + dude.scale.set(0.8 + Math.random() * 0.3); + + // Finally let's set the dude to be at a random position... + dude.x = Math.floor(Math.random() * app.screen.width); + dude.y = Math.floor(Math.random() * app.screen.height); + + // The important bit of this example, this is how you change the default blend mode of the sprite + dude.blendMode = 'add'; + + // Create some extra properties that will control movement + dude.direction = Math.random() * Math.PI * 2; + + // This number will be used to modify the direction of the dude over time + dude.turningSpeed = Math.random() - 0.8; + + // Create a random speed for the dude between 0 - 2 + dude.speed = 2 + Math.random() * 2; + + // Finally we push the dude into the dudeArray so it it can be easily accessed later + dudeArray.push(dude); + + app.stage.addChild(dude); + } + + // Create a bounding box for the little dudes + const dudeBoundsPadding = 100; + + const dudeBounds = new Rectangle( + -dudeBoundsPadding, + -dudeBoundsPadding, + app.screen.width + dudeBoundsPadding * 2, + app.screen.height + dudeBoundsPadding * 2, + ); + + app.ticker.add(() => + { + // Iterate through the dudes and update the positions + for (let i = 0; i < dudeArray.length; i++) + { + const dude = dudeArray[i]; + + dude.direction += dude.turningSpeed * 0.01; + dude.x += Math.sin(dude.direction) * dude.speed; + dude.y += Math.cos(dude.direction) * dude.speed; + dude.rotation = -dude.direction - Math.PI / 2; + + // Constrain the dudes' position by testing their bounds... + if (dude.x < dudeBounds.x) + { + dude.x += dudeBounds.width; + } + else if (dude.x > dudeBounds.x + dudeBounds.width) + { + dude.x -= dudeBounds.width; + } + + if (dude.y < dudeBounds.y) + { + dude.y += dudeBounds.height; + } + else if (dude.y > dudeBounds.y + dudeBounds.height) + { + dude.y -= dudeBounds.height; + } + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/container.js b/src/examples/v8.0.0-rc.1/basic/container.js new file mode 100644 index 000000000..c9ff9506e --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/container.js @@ -0,0 +1,47 @@ +import { Application, Assets, Container, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Create and add a container to the stage + const container = new Container(); + + app.stage.addChild(container); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create a 5x5 grid of bunnies in the container + for (let i = 0; i < 25; i++) + { + const bunny = new Sprite(texture); + + bunny.x = (i % 5) * 40; + bunny.y = Math.floor(i / 5) * 40; + container.addChild(bunny); + } + + // Move the container to the center + container.x = app.screen.width / 2; + container.y = app.screen.height / 2; + + // Center the bunny sprites in local container coordinates + container.pivot.x = container.width / 2; + container.pivot.y = container.height / 2; + + // Listen for animate update + app.ticker.add((time) => + { + // Continuously rotate the container! + // * use delta to create frame-independent transform * + container.rotation -= 0.01 * time.deltaTime; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/fillGradient.js b/src/examples/v8.0.0-rc.1/basic/fillGradient.js new file mode 100644 index 000000000..51fa8a58c --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/fillGradient.js @@ -0,0 +1,56 @@ +import { Application, FillGradient, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Create a color array for the linear gradient + const colorStops = [0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0x000000]; + + // Create a fill gradient + const gradientFill = new FillGradient(0, 0, 150, 150); + + // Add the color stops to the fill gradient + colorStops.forEach((number, index) => + { + const ratio = index / colorStops.length; + + gradientFill.addColorStop(ratio, number); + }); + + // Create a fill graphic + const graphic1 = new Graphics().roundRect(0, 0, 150, 150, 50).fill(gradientFill); + + // Create a stroke graphic + const graphic2 = new Graphics().roundRect(0, 0, 150, 150, 50).stroke({ width: 20, fill: gradientFill }); + + graphic1.pivot.set(75, 75); + graphic1.x = 150; + graphic1.y = 200; + + graphic2.x = 350; + graphic2.y = 125; + + app.stage.addChild(graphic1); + app.stage.addChild(graphic2); + + let tick = 0; + + // Animate the graphics + app.ticker.add(() => + { + tick += 0.025; + graphic1.rotation += 0.01; + graphic2 + .clear() + .roundRect(0, 0, 150, 150, 50) + .stroke({ width: Math.sin(tick) * 100, fill: gradientFill }); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/meshPlane.js b/src/examples/v8.0.0-rc.1/basic/meshPlane.js new file mode 100644 index 000000000..4e1ef63d5 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/meshPlane.js @@ -0,0 +1,41 @@ +import { Application, MeshPlane, Assets } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the grass texture + const texture = await Assets.load('https://pixijs.com/assets/bg_grass.jpg'); + + // Create a simple grass plane and add it to the stage + const plane = new MeshPlane({ texture, verticesX: 10, verticesY: 10 }); + + plane.x = 100; + plane.y = 100; + + app.stage.addChild(plane); + + // Get the buffer for vertex positions. + const { buffer } = plane.geometry.getAttribute('aPosition'); + + // Listen for animate update + let timer = 0; + + app.ticker.add(() => + { + // Randomize the vertice positions a bit to create movement. + for (let i = 0; i < buffer.data.length; i++) + { + buffer.data[i] += Math.sin(timer / 10 + i) * 0.5; + } + buffer.update(); + timer++; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/particleContainer.js b/src/examples/v8.0.0-rc.1/basic/particleContainer.js new file mode 100644 index 000000000..f37c0d097 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/particleContainer.js @@ -0,0 +1,108 @@ +import { Application, Assets, Sprite, Container, Rectangle } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the maggot texture + const texture = await Assets.load('https://pixijs.com/assets/maggot_tiny.png'); + + // Create a container for all the maggots particles and add it to the stage + const sprites = new Container(); + + app.stage.addChild(sprites); + + // Create an array to store all the sprites + const maggots = []; + + const totalSprites = 10000; + + for (let i = 0; i < totalSprites; i++) + { + // Create a new maggot Sprite + const dude = new Sprite(texture); + + // Set the anchor point so the texture is centerd on the sprite + dude.anchor.set(0.5); + + // Different maggots, different sizes + dude.scale.set(0.8 + Math.random() * 0.3); + + // Scatter them all + dude.x = Math.random() * app.screen.width; + dude.y = Math.random() * app.screen.height; + + dude.tint = Math.random() * 0x808080; + + // Create a random direction in radians + dude.direction = Math.random() * Math.PI * 2; + + // This number will be used to modify the direction of the sprite over time + dude.turningSpeed = Math.random() - 0.8; + + // Create a random speed between 0 - 2, and these maggots are slooww + dude.speed = (2 + Math.random() * 2) * 0.2; + + dude.offset = Math.random() * 100; + + // Finally we push the dude into the maggots array so it it can be easily accessed later + maggots.push(dude); + + sprites.addChild(dude); + } + + // Create a bounding box box for the little maggots + const dudeBoundsPadding = 100; + const dudeBounds = new Rectangle( + -dudeBoundsPadding, + -dudeBoundsPadding, + app.screen.width + dudeBoundsPadding * 2, + app.screen.height + dudeBoundsPadding * 2, + ); + + let tick = 0; + + app.ticker.add(() => + { + // Iterate through the sprites and update their position + for (let i = 0; i < maggots.length; i++) + { + const dude = maggots[i]; + + dude.scale.y = 0.95 + Math.sin(tick + dude.offset) * 0.05; + dude.direction += dude.turningSpeed * 0.01; + dude.x += Math.sin(dude.direction) * (dude.speed * dude.scale.y); + dude.y += Math.cos(dude.direction) * (dude.speed * dude.scale.y); + dude.rotation = -dude.direction + Math.PI; + + // Wrap the maggots + if (dude.x < dudeBounds.x) + { + dude.x += dudeBounds.width; + } + else if (dude.x > dudeBounds.x + dudeBounds.width) + { + dude.x -= dudeBounds.width; + } + + if (dude.y < dudeBounds.y) + { + dude.y += dudeBounds.height; + } + else if (dude.y > dudeBounds.y + dudeBounds.height) + { + dude.y -= dudeBounds.height; + } + } + + // Increment the ticker + tick += 0.1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/tinting.js b/src/examples/v8.0.0-rc.1/basic/tinting.js new file mode 100644 index 000000000..56c00684a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/tinting.js @@ -0,0 +1,98 @@ +import { Application, Assets, Sprite, Rectangle } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the alien texture + const texture = await Assets.load('https://pixijs.com/assets/eggHead.png'); + + // Create an array to store the aliens + const aliens = []; + + const totalDudes = 20; + + for (let i = 0; i < totalDudes; i++) + { + // Create a new alien Sprite + const dude = new Sprite(texture); + + // Set the anchor point so the texture is centered on the sprite + dude.anchor.set(0.5); + + // Set a random scale for the dude - no point them all being the same size! + dude.scale.set(0.8 + Math.random() * 0.3); + + // Finally lets set the dude to be at a random position.. + dude.x = Math.random() * app.screen.width; + dude.y = Math.random() * app.screen.height; + + dude.tint = Math.random() * 0xffffff; + + // Create some extra properties that will control movement: --------------------- + + // Create a random direction in radians. + // This is a number between 0 and PI*2 which is the equivalent of 0 - 360 degrees + dude.direction = Math.random() * Math.PI * 2; + + // This number will be used to modify the direction of the dude over time + dude.turningSpeed = Math.random() - 0.8; + + // Create a random speed for the dude between 2 - 4 + dude.speed = 2 + Math.random() * 2; + + // Finally we push the dude into the aliens array so it it can be easily accessed later + aliens.push(dude); + + app.stage.addChild(dude); + } + + // Create a bounding box for the little dudes + const dudeBoundsPadding = 100; + const dudeBounds = new Rectangle( + -dudeBoundsPadding, + -dudeBoundsPadding, + app.screen.width + dudeBoundsPadding * 2, + app.screen.height + dudeBoundsPadding * 2, + ); + + app.ticker.add(() => + { + // Iterate through the dudes and update their position + for (let i = 0; i < aliens.length; i++) + { + const dude = aliens[i]; + + dude.direction += dude.turningSpeed * 0.01; + dude.x += Math.sin(dude.direction) * dude.speed; + dude.y += Math.cos(dude.direction) * dude.speed; + dude.rotation = -dude.direction - Math.PI / 2; + + // Constrain the dudes' position by testing their bounds... + if (dude.x < dudeBounds.x) + { + dude.x += dudeBounds.width; + } + else if (dude.x > dudeBounds.x + dudeBounds.width) + { + dude.x -= dudeBounds.width; + } + + if (dude.y < dudeBounds.y) + { + dude.y += dudeBounds.height; + } + else if (dude.y > dudeBounds.y + dudeBounds.height) + { + dude.y -= dudeBounds.height; + } + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/basic/transparentBackground.js b/src/examples/v8.0.0-rc.1/basic/transparentBackground.js new file mode 100644 index 000000000..407c328c3 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/basic/transparentBackground.js @@ -0,0 +1,35 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application with a transparent background + await app.init({ backgroundAlpha: 0, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create a new Sprite with the texture + const bunny = new Sprite(texture); + + // Center the sprite's anchor point + bunny.anchor.set(0.5); + + // Move the sprite to the center of the screen + bunny.x = app.screen.width / 2; + bunny.y = app.screen.height / 2; + + app.stage.addChild(bunny); + + // Listen for animate update + app.ticker.add(() => + { + // Just for fun, let's rotate our bunny over time! + bunny.rotation += 0.1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/events/click.js b/src/examples/v8.0.0-rc.1/events/click.js new file mode 100644 index 000000000..d7fd0ce3a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/click.js @@ -0,0 +1,48 @@ +import { Application, Assets, Sprite, settings, SCALE_MODES } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Set the texture's scale mode to nearest to preserve pixelation + texture.baseTexture.scaleMode = SCALE_MODES.NEAREST; + + // Create the bunny sprite + const sprite = Sprite.from(texture); + + // Set the initial position + sprite.anchor.set(0.5); + sprite.x = app.screen.width / 2; + sprite.y = app.screen.height / 2; + + // Opt-in to interactivity + sprite.eventMode = 'static'; + + // Shows hand cursor + sprite.cursor = 'pointer'; + + // Pointers normalize touch and mouse (good for mobile and desktop) + sprite.on('pointerdown', onClick); + + // Alternatively, use the mouse & touch events: + // sprite.on('click', onClick); // mouse-only + // sprite.on('tap', onClick); // touch-only + + app.stage.addChild(sprite); + + function onClick() + { + sprite.scale.x *= 1.25; + sprite.scale.y *= 1.25; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/events/customHitarea.js b/src/examples/v8.0.0-rc.1/events/customHitarea.js new file mode 100644 index 000000000..c1359ca73 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/customHitarea.js @@ -0,0 +1,131 @@ +import { Application, Assets, Sprite, Graphics, Polygon, Text, TextStyle } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the star texture + const starTexture = await Assets.load('https://pixijs.com/assets/yellowstar.png'); + + // Standard Sprite Button + const starButton1 = new Sprite(starTexture); + + starButton1.position.set(50, 200); + starButton1.cursor = 'pointer'; + starButton1.eventMode = 'static'; + + starButton1 + .on('pointerdown', onClick, starButton1) + .on('pointerover', onPointerOver, starButton1) + .on('pointerout', onPointerOut, starButton1); + + // Custom Hitarea Button + const starButton2 = new Sprite(starTexture); + + starButton2.position.set(250, 200); + + // Create a hitarea that matches the sprite, which will be used for point intersection + starButton2.hitArea = new Polygon([80, 0, 100, 50, 160, 55, 115, 95, 130, 150, 80, 120, 30, 150, 45, 95, 0, 55, 60, 50]); + starButton2.cursor = 'pointer'; + starButton2.eventMode = 'static'; + + starButton2 + .on('pointerdown', onClick, starButton2) + .on('pointerover', onPointerOver, starButton2) + .on('pointerout', onPointerOut, starButton2); + + // With Mask, No Hit Area + const starButton3 = new Sprite(starTexture); + + starButton3.position.set(450, 200); + starButton3.cursor = 'pointer'; + starButton3.eventMode = 'static'; + + const squareMask = new Graphics().rect(starButton3.x, starButton3.y, 75, 200).fill({ color: 0xffffff }); + + starButton3.mask = squareMask; + + starButton3 + .on('pointerdown', onClick, starButton3) + .on('pointerover', onPointerOver, starButton3) + .on('pointerout', onPointerOut, starButton3); + + // With a Mask and Hit Area + // Hitareas ignore masks. You can still click on a button made in this way, + // even from areas covered by a mask + const starButton4 = new Sprite(starTexture); + + starButton4.position.set(600, 200); + + const squareMask2 = new Graphics().rect(starButton4.x, starButton4.y, 75, 200).fill({ color: 0xffffff }); + + starButton4.mask = squareMask2; + + // Again, hitarea for intersection checks + starButton4.hitArea = new Polygon([80, 0, 100, 50, 160, 55, 115, 95, 130, 150, 80, 120, 30, 150, 45, 95, 0, 55, 60, 50]); + starButton4.cursor = 'pointer'; + starButton4.eventMode = 'static'; + + starButton4 + .on('pointerdown', onClick, starButton4) + .on('pointerover', onPointerOver, starButton4) + .on('pointerout', onPointerOut, starButton4); + + const style = new TextStyle({ fill: '#ffffff' }); + + const text1 = new Text({ text: 'Standard', style }); + + text1.x = starButton1.x + 25; + text1.y = starButton1.y + 170; + + const text2 = new Text({ text: 'Hit Area', style }); + + text2.x = starButton2.x + 35; + text2.y = starButton2.y + 170; + + const text3 = new Text({ text: 'Mask', style }); + + text3.x = starButton3.x + 10; + text3.y = starButton3.y + 170; + + const text4 = new Text({ text: 'Mask + Hit Area', style }); + + text4.x = starButton4.x - 10; + text4.y = starButton4.y + 170; + + // Add to stage + app.stage.addChild( + starButton2, + starButton1, + starButton3, + starButton4, + squareMask, + squareMask2, + text1, + text2, + text3, + text4, + ); + + function onClick() + { + this.tint = 0x333333; + } + + function onPointerOver() + { + this.tint = 0x666666; + } + + function onPointerOut() + { + this.tint = 0xffffff; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/events/customMouseIcon.js b/src/examples/v8.0.0-rc.1/events/customMouseIcon.js new file mode 100644 index 000000000..74a949c8e --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/customMouseIcon.js @@ -0,0 +1,121 @@ +import { Application, Assets, Sprite, Texture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // CSS style for icons + const defaultIcon = 'url(\'https://pixijs.com/assets/bunny.png\'),auto'; + const hoverIcon = 'url(\'https://pixijs.com/assets/bunny_saturated.png\'),auto'; + + // Load textures + await Assets.load([ + 'https://pixijs.com/assets/bg_button.jpg', + 'https://pixijs.com/assets/button.png', + 'https://pixijs.com/assets/button_down.png', + 'https://pixijs.com/assets/button_over.png', + ]); + + // Add custom cursor styles + app.renderer.events.cursorStyles.default = defaultIcon; + app.renderer.events.cursorStyles.hover = hoverIcon; + + // Create a background... + const background = Sprite.from('https://pixijs.com/assets/bg_button.jpg'); + + background.width = app.screen.width; + background.height = app.screen.height; + + // Add background to stage... + app.stage.addChild(background); + + // Create some textures from an image path + const textureButton = Texture.from('https://pixijs.com/assets/button.png'); + const textureButtonDown = Texture.from('https://pixijs.com/assets/button_down.png'); + const textureButtonOver = Texture.from('https://pixijs.com/assets/button_over.png'); + + const buttons = []; + + const buttonPositions = [175, 75, 655, 75, 410, 325, 150, 465, 685, 445]; + + for (let i = 0; i < 5; i++) + { + const button = new Sprite(textureButton); + + button.cursor = 'hover'; + + button.anchor.set(0.5); + button.x = buttonPositions[i * 2]; + button.y = buttonPositions[i * 2 + 1]; + + // Make the button interactive... + button.eventMode = 'static'; + + button + .on('pointerdown', onButtonDown) + .on('pointerup', onButtonUp) + .on('pointerupoutside', onButtonUp) + .on('pointerover', onButtonOver) + .on('pointerout', onButtonOut); + + // Add it to the stage + app.stage.addChild(button); + + // Add button to array + buttons.push(button); + } + + // Set some silly values... + buttons[0].scale.set(1.2); + buttons[2].rotation = Math.PI / 10; + buttons[3].scale.set(0.8); + buttons[4].scale.set(0.8, 1.2); + buttons[4].rotation = Math.PI; + + function onButtonDown() + { + this.isdown = true; + this.texture = textureButtonDown; + this.alpha = 1; + } + + function onButtonUp() + { + this.isdown = false; + if (this.isOver) + { + this.texture = textureButtonOver; + } + else + { + this.texture = textureButton; + } + } + + function onButtonOver() + { + this.isOver = true; + if (this.isdown) + { + return; + } + this.texture = textureButtonOver; + } + + function onButtonOut() + { + this.isOver = false; + if (this.isdown) + { + return; + } + this.texture = textureButton; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/events/dragging.js b/src/examples/v8.0.0-rc.1/events/dragging.js new file mode 100644 index 000000000..35288a5e1 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/dragging.js @@ -0,0 +1,87 @@ +import { Application, Assets, Sprite, SCALE_MODES } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Set the texture's scale mode to nearest to preserve pixelation + texture.baseTexture.scaleMode = SCALE_MODES.NEAREST; + + for (let i = 0; i < 10; i++) + { + createBunny(Math.floor(Math.random() * app.screen.width), Math.floor(Math.random() * app.screen.height)); + } + + function createBunny(x, y) + { + // Create our little bunny friend.. + const bunny = new Sprite(texture); + + // Enable the bunny to be interactive... this will allow it to respond to mouse and touch events + bunny.eventMode = 'static'; + + // This button mode will mean the hand cursor appears when you roll over the bunny with your mouse + bunny.cursor = 'pointer'; + + // Center the bunny's anchor point + bunny.anchor.set(0.5); + + // Make it a bit bigger, so it's easier to grab + bunny.scale.set(3); + + // Setup events for mouse + touch using the pointer events + bunny.on('pointerdown', onDragStart, bunny); + + // Move the sprite to its designated position + bunny.x = x; + bunny.y = y; + + // Add it to the stage + app.stage.addChild(bunny); + } + + let dragTarget = null; + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage.on('pointerup', onDragEnd); + app.stage.on('pointerupoutside', onDragEnd); + + function onDragMove(event) + { + if (dragTarget) + { + dragTarget.parent.toLocal(event.global, null, dragTarget.position); + } + } + + function onDragStart() + { + // Store a reference to the data + // * The reason for this is because of multitouch * + // * We want to track the movement of this particular touch * + this.alpha = 0.5; + dragTarget = this; + app.stage.on('pointermove', onDragMove); + } + + function onDragEnd() + { + if (dragTarget) + { + app.stage.off('pointermove', onDragMove); + dragTarget.alpha = 1; + dragTarget = null; + } + } +})(); diff --git a/src/examples/v8.0.0-rc/events/hitTestingWithSpatialHash.js b/src/examples/v8.0.0-rc.1/events/hitTestingWithSpatialHash.js similarity index 100% rename from src/examples/v8.0.0-rc/events/hitTestingWithSpatialHash.js rename to src/examples/v8.0.0-rc.1/events/hitTestingWithSpatialHash.js diff --git a/src/examples/v8.0.0-rc.1/events/interactivity.js b/src/examples/v8.0.0-rc.1/events/interactivity.js new file mode 100644 index 000000000..d3a9f7b10 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/interactivity.js @@ -0,0 +1,127 @@ +import { Application, Assets, Sprite, Texture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load textures + await Assets.load([ + 'https://pixijs.com/assets/bg_button.jpg', + 'https://pixijs.com/assets/button.png', + 'https://pixijs.com/assets/button_down.png', + 'https://pixijs.com/assets/button_over.png', + ]); + + // Create a background... + const background = Sprite.from('https://pixijs.com/assets/bg_button.jpg'); + + background.width = app.screen.width; + background.height = app.screen.height; + + // Add background to stage... + app.stage.addChild(background); + + // Create some textures from an image path + const textureButton = Texture.from('https://pixijs.com/assets/button.png'); + const textureButtonDown = Texture.from('https://pixijs.com/assets/button_down.png'); + const textureButtonOver = Texture.from('https://pixijs.com/assets/button_over.png'); + + const buttons = []; + + const buttonPositions = [175, 75, 655, 75, 410, 325, 150, 465, 685, 445]; + + for (let i = 0; i < 5; i++) + { + const button = new Sprite(textureButton); + + button.anchor.set(0.5); + button.x = buttonPositions[i * 2]; + button.y = buttonPositions[i * 2 + 1]; + + // Make the button interactive... + button.eventMode = 'static'; + button.cursor = 'pointer'; + + button + // Mouse & touch events are normalized into + // the pointer* events for handling different + // button events. + .on('pointerdown', onButtonDown) + .on('pointerup', onButtonUp) + .on('pointerupoutside', onButtonUp) + .on('pointerover', onButtonOver) + .on('pointerout', onButtonOut); + + // Use mouse-only events + // .on('mousedown', onButtonDown) + // .on('mouseup', onButtonUp) + // .on('mouseupoutside', onButtonUp) + // .on('mouseover', onButtonOver) + // .on('mouseout', onButtonOut) + + // Use touch-only events + // .on('touchstart', onButtonDown) + // .on('touchend', onButtonUp) + // .on('touchendoutside', onButtonUp) + + // Add it to the stage + app.stage.addChild(button); + + // Add button to array + buttons.push(button); + } + + // Set some silly values... + buttons[0].scale.set(1.2); + buttons[2].rotation = Math.PI / 10; + buttons[3].scale.set(0.8); + buttons[4].scale.set(0.8, 1.2); + buttons[4].rotation = Math.PI; + + function onButtonDown() + { + this.isdown = true; + this.texture = textureButtonDown; + this.alpha = 1; + } + + function onButtonUp() + { + this.isdown = false; + if (this.isOver) + { + this.texture = textureButtonOver; + } + else + { + this.texture = textureButton; + } + } + + function onButtonOver() + { + this.isOver = true; + if (this.isdown) + { + return; + } + this.texture = textureButtonOver; + } + + function onButtonOut() + { + this.isOver = false; + if (this.isdown) + { + return; + } + this.texture = textureButton; + } +})(); diff --git a/src/examples/v8.0.0-rc.1/events/logger.js b/src/examples/v8.0.0-rc.1/events/logger.js new file mode 100644 index 000000000..e0cb5bef2 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/logger.js @@ -0,0 +1,93 @@ +import { Application, Assets, Text, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const title = app.stage.addChild( + new Text({ + text: `Move your mouse slowly over the boxes to + see the order of pointerenter, pointerleave, + pointerover, pointerout events on each target!`, + style: { + fontSize: 16, + }, + }), + ); + + title.x = 2; + + const logs = []; + const logText = app.stage.addChild( + new Text({ + text: '', + style: { + fontSize: 14, + }, + }), + ); + + logText.y = 80; + logText.x = 2; + + app.stage.name = 'stage'; + + // Mount outer black box + const blackBox = app.stage.addChild(new Graphics().rect(0, 0, 400, 400).fill({ color: 0 })); + + blackBox.name = 'black box'; + blackBox.x = 400; + + // Mount white box inside the white one + const whiteBox = blackBox.addChild(new Graphics().rect(100, 100, 200, 200).fill({ color: 0xffffff })); + + whiteBox.name = 'white box'; + + // Enable interactivity everywhere! + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + whiteBox.eventMode = 'static'; + blackBox.eventMode = 'static'; + + function onEvent(e) + { + const type = e.type; + const targetName = e.target.name; + const currentTargetName = e.currentTarget.name; + + // Add event to top of logs + logs.push(`${currentTargetName} received ${type} event (target is ${targetName})`); + + if (currentTargetName === 'stage' || type === 'pointerenter' || type === 'pointerleave') + { + logs.push('-----------------------------------------', ''); + } + + // Prevent logs from growing too long + if (logs.length > 30) + { + while (logs.length > 30) + { + logs.shift(); + } + } + + // Update logText + logText.text = logs.join('\n'); + } + + [app.stage, whiteBox, blackBox].forEach((object) => + { + object.addEventListener('pointerenter', onEvent); + object.addEventListener('pointerleave', onEvent); + object.addEventListener('pointerover', onEvent); + object.addEventListener('pointerout', onEvent); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/events/nestedBoundaryWithProjection.js b/src/examples/v8.0.0-rc.1/events/nestedBoundaryWithProjection.js new file mode 100644 index 000000000..f35c97567 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/nestedBoundaryWithProjection.js @@ -0,0 +1,230 @@ +import { Application, Container, Sprite, Matrix, EventBoundary, Graphics, Rectangle, Text } from 'pixi.js'; + +// This example shows how you can setup a nested boundary to propagate events +// into a disjoint scene graph. Here, a camera is used to project an different +// world onto the canvas. + +// A projector renders it's content using projection. The transforms in +// the contents scene graph don't change if you move the camera. To achieve +// this, the content is not added as a "child" to the projector; however, this +// means events won't propagate into the content by default. +// +// To solve this, we nest our own EventBoundary, and connect it using +// addEventListener! +class Projector extends Container +{ + constructor() + { + super(); + + // The content root to be rendered by this camera. + this.content = new Container(); + + // Temporary matrix to store the original projection transform. + this.originalTransform = new Matrix(); + + // The event boundary that'll map events downstream into the content + // scene. + this.boundary = new EventBoundary(this.content); + + // Override copyMouseData to apply inverse worldTransform on + // global coords + this.boundary.copyMouseData = (from, to) => + { + // Apply default implementation first + EventBoundary.prototype.copyMouseData.call(this.boundary, from, to); + + // Then bring global coords into content's world + this.worldTransform.applyInverse(to.global, to.global); + }; + + // Propagate these events down into the content's scene graph! + ['pointerdown', 'pointerup', 'pointermove', 'pointerover', 'pointerout', 'wheel'].forEach((event) => + { + this.addEventListener(event, (e) => this.boundary.mapEvent(e)); + }); + + this.eventMode = 'static'; + } + + // Pass through cursor + get cursor() + { + return this.boundary.cursor; + } + + // eslint-disable-next-line class-methods-use-this + set cursor(value) + { + throw new Error('The camera\'s cursor is derived from its content!'); + } + + // Pass through calculateBounds + calculateBounds() + { + const contentBounds = this.content.getBounds(); + + this._bounds.addFrameMatrix( + this.worldTransform, + contentBounds.x, + contentBounds.y, + contentBounds.width, + contentBounds.height, + ); + } + + // Pass through containsPoint + containsPoint(point) + { + return !!this.boundary.hitTest(point.x, point.y); + } + + // Render content with projection + render(renderer) + { + renderer.batch.flush(); + + const projectionSystem = renderer.projection; + const renderTextureSystem = renderer.renderTexture; + + projectionSystem.transform = projectionSystem.transform || new Matrix(); + projectionSystem.transform.copyTo(this.originalTransform); + projectionSystem.transform.append(this.worldTransform); + projectionSystem.update(null, null, 1, !renderTextureSystem.current); + + this.content.render(renderer); + + renderer.batch.flush(); + + projectionSystem.transform.copyFrom(this.originalTransform); + projectionSystem.update(null, null, 1, !renderTextureSystem.current); + } + + // updateTransform also updates content's transform + updateTransform() + { + super.updateTransform(); + + this.content.enableTempParent(); + this.content.updateTransform(); + this.content.disableTempParent(null); + } +} + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // The projector + const projector = app.stage.addChild(new Projector()); + + // Add coordinate axes! + projector.content.addChild( + new Graphics() + .moveTo(0, -300) + .lineTo(0, 600) + .moveTo(-100, 0) + .lineTo(700, 0) + .stroke({ color: 0, alpha: 0.2, width: 2 }), + ); + + // Construct the star Graphics + const stars = [1, 2, 3].map((i) => + new Graphics().star(0, 0, 18 / i, (100 * i) / 2).fill({ color: 0xffffff, alpha: 0.75 }), + ); + + // Place the stars + stars[0].x = 0; + stars[1].x = 200; + stars[2].x = 500; + + // Add stars to the projector + projector.content.addChild(...stars); + + // Make projection x+100, y+300 + projector.x = 100; + projector.y = 300; + projector.content.hitArea = new Rectangle(-100, -300, app.screen.width, app.screen.height); + // Make hit-area cover the whole screen so we can capture + // pointermove everywhere! + projector.hitArea = projector.content.hitArea; + projector.content.eventMode = 'static'; + + // Make stars interactive & add wheel handlers + stars.forEach((star) => + { + // Make star interactive + star.eventMode = 'static'; + + // Set initial cursor + star.cursor = 'zoom-in'; + + // Add wheel rotation feedback + star.addEventListener('wheel', (e) => + { + const scroll = Math.sign(e.deltaY) * Math.min(15, Math.abs(e.deltaY)); + + star.rotation += scroll / 100; + }); + + // Add click zoom-in/zoom-out handler + star.addEventListener('click', (e) => + { + if (star.scale.x === 1) + { + star.scale.set(1.33); + star.cursor = 'zoom-out'; + } + else + { + star.scale.set(1); + star.cursor = 'zoom-in'; + } + }); + }); + + const coordinates = new Text({ + text: 'Global: (0, 0)\nScreen: (0, 0)', + style: { + fontSize: 16, + fontFamily: 'Roboto', + fill: '#272d37', + }, + }); + + coordinates.x = 110; + coordinates.y = 550; + + app.stage.addChild(coordinates); + + projector.content.addEventListener('pointermove', (e) => + { + const global = `(${e.global.x | 0}, ${e.global.y | 0})`; + const screen = `(${e.screen.x | 0}, ${e.screen.y | 0})`; + + coordinates.text = `Global: ${global}\nScreen: ${screen}`; + }); + + const description = new Text({ + text: + 'The (0, 0) world coordinates for the content is located at the center of the first star!' + + '\n * Mouse wheel over stars to rotate them' + + '\n * Click to zoom in or out', + style: { + fontSize: 16, + fontFamily: 'Roboto', + fill: '#272d37', + }, + }); + + description.position.set(110, 12); + + app.stage.addChild(description); +})(); diff --git a/src/examples/v8.0.0-rc.1/events/pointerTracker.js b/src/examples/v8.0.0-rc.1/events/pointerTracker.js new file mode 100644 index 000000000..aaf48d5f0 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/pointerTracker.js @@ -0,0 +1,32 @@ +import { Application, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Create the circle + const circle = app.stage.addChild( + new Graphics().circle(0, 0, 8).fill({ color: 0xffffff }).stroke({ color: 0x111111, alpha: 0.87, width: 1 }), + ); + + circle.position.set(app.screen.width / 2, app.screen.height / 2); + + // Enable interactivity! + app.stage.eventMode = 'static'; + + // Make sure the whole canvas area is interactive, not just the circle. + app.stage.hitArea = app.screen; + + // Follow the pointer + app.stage.addEventListener('pointermove', (e) => + { + circle.position.copyFrom(e.global); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/events/slider.js b/src/examples/v8.0.0-rc.1/events/slider.js new file mode 100644 index 000000000..12b5ec805 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/events/slider.js @@ -0,0 +1,95 @@ +import { Application, Assets, Text, Graphics, Sprite, SCALE_MODES } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const stageHeight = app.screen.height; + const stageWidth = app.screen.width; + + // Make sure stage covers the whole scene + app.stage.hitArea = app.screen; + + // Make the slider + const sliderWidth = 320; + const slider = new Graphics().rect(0, 0, sliderWidth, 4).fill({ color: 0x272d37 }); + + slider.x = (stageWidth - sliderWidth) / 2; + slider.y = stageHeight * 0.75; + + // Draw the handle + const handle = new Graphics().circle(0, 0, 8).fill({ color: 0xffffff }); + + handle.y = slider.height / 2; + handle.x = sliderWidth / 2; + handle.eventMode = 'static'; + handle.cursor = 'pointer'; + + handle.on('pointerdown', onDragStart).on('pointerup', onDragEnd).on('pointerupoutside', onDragEnd); + + app.stage.addChild(slider); + slider.addChild(handle); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Add bunny whose scale can be changed by user using slider + const bunny = app.stage.addChild(Sprite.from(texture)); + + bunny.texture.baseTexture.scaleMode = SCALE_MODES.NEAREST; + bunny.scale.set(3); + bunny.anchor.set(0.5); + bunny.x = stageWidth / 2; + bunny.y = stageHeight / 2; + + // Add title + const title = new Text({ + text: 'Drag the handle to change the scale of bunny.', + style: { + fill: '#272d37', + fontFamily: 'Roboto', + fontSize: 20, + align: 'center', + }, + }); + + title.roundPixels = true; + title.x = stageWidth / 2; + title.y = 40; + title.anchor.set(0.5, 0); + app.stage.addChild(title); + + // Listen to pointermove on stage once handle is pressed. + function onDragStart() + { + app.stage.eventMode = 'static'; + app.stage.addEventListener('pointermove', onDrag); + } + + // Stop dragging feedback once the handle is released. + function onDragEnd(e) + { + app.stage.eventMode = 'auto'; + app.stage.removeEventListener('pointermove', onDrag); + } + + // Update the handle's position & bunny's scale when the handle is moved. + function onDrag(e) + { + const halfHandleWidth = handle.width / 2; + // Set handle y-position to match pointer, clamped to (4, screen.height - 4). + + handle.x = Math.max(halfHandleWidth, Math.min(slider.toLocal(e.global).x, sliderWidth - halfHandleWidth)); + // Normalize handle position between -1 and 1. + const t = 2 * (handle.x / sliderWidth - 0.5); + + bunny.scale.set(3 * (1.1 + t)); + } +})(); diff --git a/src/examples/v8.0.0-rc.1/examplesData.json b/src/examples/v8.0.0-rc.1/examplesData.json new file mode 100644 index 000000000..0d83ababf --- /dev/null +++ b/src/examples/v8.0.0-rc.1/examplesData.json @@ -0,0 +1,105 @@ +{ + "basic": [ + "container", + "transparentBackground", + "tinting", + "particleContainer", + "blendModes", + "meshPlane", + "fillGradient" + ], + "advanced": [ + "slots", + "scratchCard", + "starWarp", + "mouseTrail", + "screenShot", + "collisionDetection", + "spinners" + ], + "sprite": [ + "basic", + "textureSwap", + "animatedSpriteExplosion", + "animatedSpriteJet", + "animatedSpriteAnimationSpeed", + "tilingSprite", + "video" + ], + "text": [ + "pixiText", + "bitmapText", + "fromFont", + "webFont" + ], + "graphics": [ + "simple", + "advanced", + "dynamic" + ], + "events": [ + "click", + "interactivity", + "dragging", + "customMouseIcon", + "customHitarea", + { + "name": "hitTestingWithSpatialHash", + "hide": true + }, + "logger", + "nestedBoundaryWithProjection", + "pointerTracker", + "slider" + ], + "masks": [ + "graphics", + "sprite", + "filter" + ], + "filtersBasic": [ + "blur", + "colorMatrix", + "displacementMapCrawlies", + "displacementMapFlag" + ], + "filtersAdvanced": [ + "mouseBlending", + "custom", + "shaderToyFilterRenderTexture" + ], + "meshAndShaders": [ + "texturedMeshBasic", + "texturedMeshAdvanced", + "triangle", + "triangleColor", + "triangleTextured", + "uniforms", + "sharingGeometry", + "sharedShader", + "mergingGeometry", + "instancedGeometry", + "shaderToyMesh", + "multiPassShaderGeneratedMesh" + ], + "textures": [ + "textureRotate", + "renderTextureBasic", + "renderTextureAdvanced" + ], + "assets": [ + "promise", + "async", + "multiple", + "background", + "bundle" + ], + "offscreenCanvas": [ + "basic", + { + "name": "webWorker", + "hide": true, + "usesWebWorkerLibrary": true + } + ] +} diff --git a/src/examples/v8.0.0-rc.1/filtersAdvanced/custom.js b/src/examples/v8.0.0-rc.1/filtersAdvanced/custom.js new file mode 100644 index 000000000..05d475a6b --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersAdvanced/custom.js @@ -0,0 +1,58 @@ +import { Application, Assets, Sprite, Filter } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/bg_grass.jpg'); + + // Create background image + const background = Sprite.from(texture); + + background.width = app.screen.width; + background.height = app.screen.height; + app.stage.addChild(background); + + // Stop application wait for load to finish + app.stop(); + + fetch('https://pixijs.com/assets/pixi-filters/shader.frag') + .then((res) => res.text()) + .then(onLoaded); + + let filter; + + // Handle the load completed + function onLoaded(data) + { + // Create the new filter, arguments: (vertexShader, framentSource) + filter = new Filter(null, data, { + customUniform: 0.0, + }); + + // === WARNING === + // specify uniforms in filter constructor + // or set them BEFORE first use + // filter.uniforms.customUniform = 0.0 + + // Add the filter + background.filters = [filter]; + + // Resume application update + app.start(); + } + + // Animate the filter + app.ticker.add((delta) => + { + filter.uniforms.customUniform += 0.04 * delta; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersAdvanced/mouseBlending.js b/src/examples/v8.0.0-rc.1/filtersAdvanced/mouseBlending.js new file mode 100644 index 000000000..c0d152802 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersAdvanced/mouseBlending.js @@ -0,0 +1,72 @@ +import { Application, Assets, Container, Sprite, Rectangle, Filter, Point } from 'pixi.js'; + +/** + * https://github.com/pixijs/pixi.js/wiki/v5-Creating-Filters + */ + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the grass texture + const texture = await Assets.load('https://pixijs.com/assets/bg_grass.jpg'); + + // Create background image + const background = new Sprite(texture); + + background.width = app.screen.width; + background.height = app.screen.height; + app.stage.addChild(background); + + // NOTE: this shader wont work on old devices where mediump precision is forced in fragment shader + // because v5 default vertex shader uses `inputSize` in it. Same uniform in fragment and vertex shader + // cant have different precision :( + + const shaderFrag = ` + precision highp float; + + varying vec2 vTextureCoord; + + uniform vec2 mouse; + uniform vec4 inputSize; + uniform vec4 outputFrame; + uniform float time; + + void main() { + vec2 screenPos = vTextureCoord * inputSize.xy + outputFrame.xy; + if (length(mouse - screenPos) < 25.0) { + gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0) * 0.7; //yellow circle, alpha=0.7 + } else { + // blend with underlying image, alpha=0.5 + gl_FragColor = vec4( sin(time), (mouse.xy - outputFrame.xy) / outputFrame.zw, 1.0) * 0.5; + } + } + `; + + const container = new Container(); + + container.filterArea = new Rectangle(100, 100, app.screen.width - 200, app.screen.height - 200); + app.stage.addChild(container); + const filter = new Filter({ + resources: { + shader: shaderFrag, + mouse: new Point(), + }, + }); + + container.filters = [filter]; + + app.stage.hitArea = app.screen; + app.stage.eventMode = 'static'; + app.stage.on('pointermove', (event) => + { + filter.uniforms.mouse.copyFrom(event.global); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersAdvanced/shaderToyFilterRenderTexture.js b/src/examples/v8.0.0-rc.1/filtersAdvanced/shaderToyFilterRenderTexture.js new file mode 100644 index 000000000..4763c3f83 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersAdvanced/shaderToyFilterRenderTexture.js @@ -0,0 +1,125 @@ +import { Application, Assets, Text, Filter, WRAP_MODES } from 'js'; + +/** + * Please note that this is not the most optimal way of doing pure shader generated rendering and should be used when the + * scene is wanted as input texture. Check the mesh version of example for more performant version if you need only shader + * generated content. + **/ + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + Assets.load('https://pixijs.com/assets/perlin.jpg').then(onAssetsLoaded); + + let filter = null; + + const text = new Text('PixiJS', { fill: 0xffffff, fontSize: 80 }); + + text.anchor.set(0.5, 0.5); + text.position.set(app.renderer.screen.width / 2, app.renderer.screen.height / 2); + + app.stage.addChild(text); + + let totalTime = 0; + + // Fragment shader, in real use this would be much cleaner when loaded from a file + // or embedded into the application as data resource. + const fragment = `//Based on this: https://www.shadertoy.com/view/wtlSWX + + varying vec2 vTextureCoord; + uniform sampler2D uSampler; + uniform sampler2D noise; + uniform float time; + // Distance function. Just calculates the height (z) from x,y plane with really simple length check. + // Its not exact as there could be shorter distances. + vec2 dist(vec3 p) + { + float id = floor(p.x)+floor(p.y); + id = mod(id, 2.); + float h = texture2D(noise, vec2(p.x, p.y)*0.04).r*5.1; + float h2 = texture2D(uSampler, vTextureCoord).r; + return vec2(h+h2-p.z,id); + } + //Light calculation. + vec3 calclight(vec3 p, vec3 rd) + { + vec2 eps = vec2( 0., 0.001); + vec3 n = normalize( vec3( + dist(p+eps.yxx).x - dist(p-eps.yxx).x, + dist(p+eps.xyx).x - dist(p-eps.xyx).x, + dist(p+eps.xxy).x - dist(p-eps.xxy).x + )); + + vec3 d = vec3( max( 0., dot( -rd ,n))); + + return d; + } + + void main() + { + vec2 uv = vec2(vTextureCoord.x, 1.-vTextureCoord.y); + uv *=2.; + uv-=1.; + + vec3 cam = vec3(0.,time -2., -3.); + vec3 target = vec3(sin(time)*0.1, time+cos(time)+2., 0. ); + float fov = 2.2; + vec3 forward = normalize( target - cam); + vec3 up = normalize(cross( forward, vec3(0., 1.,0.))); + vec3 right = normalize( cross( up, forward)); + vec3 raydir = normalize(vec3( uv.x *up + uv.y * right + fov*forward)); + + //Do the raymarch + vec3 col = vec3(0.); + float t = 0.; + for( int i = 0; i < 100; i++) + { + vec3 p = t * raydir + cam; + vec2 d = dist(p); + t+=d.x*0.5;//Jump only half of the distance as height function used is not really the best for heightmaps. + if(d.x < 0.001) + { + vec3 bc = d.y < 0.5 ? vec3(1.0, .8, 0.) : + vec3(0.8,0.0, 1.0); + col = vec3( 1.) * calclight(p, raydir) * (1. - t/150.) *bc; + break; + } + if(t > 1000.) + { + break; + } + } + gl_FragColor = vec4(col, 1.); + } + `; + + function onAssetsLoaded(perlin) + { + // Add perlin noise for filter, make sure it's wrapping and does not have mipmap. + perlin.baseTexture.wrapMode = WRAP_MODES.REPEAT; + perlin.baseTexture.mipmap = false; + + // Build the filter + filter = new Filter(null, fragment, { + time: 0.0, + noise: perlin, + }); + app.stage.filterArea = app.renderer.screen; + app.stage.filters = [filter]; + + // Listen for animate update. + app.ticker.add((delta) => + { + filter.uniforms.time = totalTime; + totalTime += delta / 60; + }); + } +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersBasic/blur.js b/src/examples/v8.0.0-rc.1/filtersBasic/blur.js new file mode 100644 index 000000000..6f74101ed --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersBasic/blur.js @@ -0,0 +1,60 @@ +import { Application, Assets, Sprite, BlurFilter } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/pixi-filters/bg_depth_blur.jpg', + 'https://pixijs.com/assets/pixi-filters/depth_blur_dudes.jpg', + 'https://pixijs.com/assets/pixi-filters/depth_blur_moby.jpg', + ]); + + const bg = Sprite.from('https://pixijs.com/assets/pixi-filters/bg_depth_blur.jpg'); + + bg.width = app.screen.width; + bg.height = app.screen.height; + app.stage.addChild(bg); + + const littleDudes = Sprite.from('https://pixijs.com/assets/pixi-filters/depth_blur_dudes.jpg'); + + littleDudes.x = app.screen.width / 2 - 315; + littleDudes.y = 200; + app.stage.addChild(littleDudes); + + const littleRobot = Sprite.from('https://pixijs.com/assets/pixi-filters/depth_blur_moby.jpg'); + + littleRobot.x = app.screen.width / 2 - 200; + littleRobot.y = 100; + app.stage.addChild(littleRobot); + + // Create the blur filters + const blurFilter1 = new BlurFilter(); + const blurFilter2 = new BlurFilter(); + + // Apply the filters to the sprites + littleDudes.filters = [blurFilter1]; + littleRobot.filters = [blurFilter2]; + + let count = 0; + + // Animate the blur filters + app.ticker.add(() => + { + count += 0.005; + + const blurAmount = Math.cos(count); + const blurAmount2 = Math.sin(count); + + blurFilter1.blur = 20 * blurAmount; + blurFilter2.blur = 20 * blurAmount2; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersBasic/colorMatrix.js b/src/examples/v8.0.0-rc.1/filtersBasic/colorMatrix.js new file mode 100644 index 000000000..5df344e0d --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersBasic/colorMatrix.js @@ -0,0 +1,113 @@ +import { Application, Assets, Container, Sprite, Text, ColorMatrixFilter } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/bg_rotate.jpg', + 'https://pixijs.com/assets/bg_scene_rotate.jpg', + 'https://pixijs.com/assets/light_rotate_2.png', + 'https://pixijs.com/assets/light_rotate_1.png', + 'https://pixijs.com/assets/panda.png', + ]); + + app.stage.eventMode = 'static'; + + const bg = Sprite.from('https://pixijs.com/assets/bg_rotate.jpg'); + + bg.anchor.set(0.5); + + bg.x = app.screen.width / 2; + bg.y = app.screen.height / 2; + + const container = new Container(); + + container.x = app.screen.width / 2; + container.y = app.screen.height / 2; + + const bgFront = Sprite.from('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + bgFront.anchor.set(0.5); + + container.addChild(bgFront); + + const light2 = Sprite.from('https://pixijs.com/assets/light_rotate_2.png'); + + light2.anchor.set(0.5); + container.addChild(light2); + + const light1 = Sprite.from('https://pixijs.com/assets/light_rotate_1.png'); + + light1.anchor.set(0.5); + container.addChild(light1); + + const panda = Sprite.from('https://pixijs.com/assets/panda.png'); + + panda.anchor.set(0.5); + + container.addChild(panda); + + app.stage.addChild(container); + + // Create a color matrix filter + const filter = new ColorMatrixFilter(); + + // Apply the Filter + container.filters = [filter]; + + let count = 0; + let enabled = true; + + app.stage.on('pointertap', () => + { + enabled = !enabled; + container.filters = enabled ? [filter] : null; + }); + + const help = new Text({ + text: 'Click or tap to turn filters on / off.', + style: { + fontFamily: 'Arial', + fontSize: 12, + fontWeight: 'bold', + fill: 'white', + }, + }); + + help.y = app.screen.height - 25; + help.x = 10; + + app.stage.addChild(help); + + app.ticker.add(() => + { + bg.rotation += 0.01; + bgFront.rotation -= 0.01; + light1.rotation += 0.02; + light2.rotation += 0.01; + + panda.scale.x = 1 + Math.sin(count) * 0.04; + panda.scale.y = 1 + Math.cos(count) * 0.04; + + count += 0.1; + + // Animate the filter + const { matrix } = filter; + + matrix[1] = Math.sin(count) * 3; + matrix[2] = Math.cos(count); + matrix[3] = Math.cos(count) * 1.5; + matrix[4] = Math.sin(count / 3) * 2; + matrix[5] = Math.sin(count / 2); + matrix[6] = Math.sin(count / 4); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapCrawlies.js b/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapCrawlies.js new file mode 100644 index 000000000..4592fe800 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapCrawlies.js @@ -0,0 +1,129 @@ +import { Application, Assets, Container, Rectangle, Sprite, Point, DisplacementFilter } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/maggot.png', + 'https://pixijs.com/assets/pixi-filters/displace.png', + 'https://pixijs.com/assets/pixi-filters/ring.png', + 'https://pixijs.com/assets/bg_grass.jpg', + ]); + + app.stage.eventMode = 'static'; + + const container = new Container(); + + app.stage.addChild(container); + + const padding = 100; + const bounds = new Rectangle(-padding, -padding, app.screen.width + padding * 2, app.screen.height + padding * 2); + const maggots = []; + + for (let i = 0; i < 20; i++) + { + const maggot = Sprite.from('https://pixijs.com/assets/maggot.png'); + + maggot.anchor.set(0.5); + container.addChild(maggot); + + maggot.direction = Math.random() * Math.PI * 2; + maggot.speed = 1; + maggot.turnSpeed = Math.random() - 0.8; + + maggot.x = Math.random() * bounds.width; + maggot.y = Math.random() * bounds.height; + + maggot.scale.set(1 + Math.random() * 0.3); + maggot.original = new Point(); + maggot.original.copyFrom(maggot.scale); + maggots.push(maggot); + } + + const displacementSprite = Sprite.from('https://pixijs.com/assets/pixi-filters/displace.png'); + + // Create a displacement filter + const displacementFilter = new DisplacementFilter({ sprite: displacementSprite, scale: 150 }); + + app.stage.addChild(displacementSprite); + + // Apply the filter + container.filters = [displacementFilter]; + + displacementSprite.anchor.set(0.5); + + const ring = Sprite.from('https://pixijs.com/assets/pixi-filters/ring.png'); + + ring.anchor.set(0.5); + + ring.visible = false; + + app.stage.addChild(ring); + + const bg = Sprite.from('https://pixijs.com/assets/bg_grass.jpg'); + + bg.width = app.screen.width; + bg.height = app.screen.height; + + bg.alpha = 0.4; + + container.addChild(bg); + + app.stage.on('mousemove', onPointerMove).on('touchmove', onPointerMove); + + function onPointerMove(eventData) + { + ring.visible = true; + + displacementSprite.position.set(eventData.data.global.x - 25, eventData.data.global.y); + ring.position.copyFrom(displacementSprite.position); + } + + let count = 0; + + // Animate the maggots + app.ticker.add(() => + { + count += 0.05; + + for (let i = 0; i < maggots.length; i++) + { + const maggot = maggots[i]; + + maggot.direction += maggot.turnSpeed * 0.01; + maggot.x += Math.sin(maggot.direction) * maggot.speed; + maggot.y += Math.cos(maggot.direction) * maggot.speed; + + maggot.rotation = -maggot.direction - Math.PI / 2; + maggot.scale.x = maggot.original.x + Math.sin(count) * 0.2; + + // wrap the maggots around as the crawl + if (maggot.x < bounds.x) + { + maggot.x += bounds.width; + } + else if (maggot.x > bounds.x + bounds.width) + { + maggot.x -= bounds.width; + } + + if (maggot.y < bounds.y) + { + maggot.y += bounds.height; + } + else if (maggot.y > bounds.y + bounds.height) + { + maggot.y -= bounds.height; + } + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapFlag.js b/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapFlag.js new file mode 100644 index 000000000..13a910c7b --- /dev/null +++ b/src/examples/v8.0.0-rc.1/filtersBasic/displacementMapFlag.js @@ -0,0 +1,60 @@ +import { Application, Assets, Container, Sprite, WRAP_MODES, DisplacementFilter } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/pixi-filters/flag.png', + 'https://pixijs.com/assets/pixi-filters/displacement_map_repeat.jpg', + ]); + + app.stage.eventMode = 'static'; + + const container = new Container(); + + app.stage.addChild(container); + + const flag = Sprite.from('https://pixijs.com/assets/pixi-filters/flag.png'); + + container.addChild(flag); + flag.x = 100; + flag.y = 100; + + const displacementSprite = Sprite.from('https://pixijs.com/assets/pixi-filters/displacement_map_repeat.jpg'); + + // Make sure the sprite is wrapping. + displacementSprite.texture.baseTexture.wrapMode = WRAP_MODES.REPEAT; + + // Create a displacement filter + const displacementFilter = new DisplacementFilter({ sprite: displacementSprite, scale: { x: 60, y: 120 } }); + + displacementFilter.padding = 10; + + displacementSprite.position = flag.position; + + app.stage.addChild(displacementSprite); + + // Apply the filter + flag.filters = [displacementFilter]; + + app.ticker.add(() => + { + // Offset the sprite position to make vFilterCoord update to larger value. + // Repeat wrapping makes sure there's still pixels on the coordinates. + displacementSprite.x++; + // Reset x to 0 when it's over width to keep values from going to very huge numbers. + if (displacementSprite.x > displacementSprite.width) + { + displacementSprite.x = 0; + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/graphics/advanced.js b/src/examples/v8.0.0-rc.1/graphics/advanced.js new file mode 100644 index 000000000..b2a0b87c7 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/graphics/advanced.js @@ -0,0 +1,113 @@ +import { Application, Assets, Graphics, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load texture + const texture = await Assets.load('https://pixijs.com/assets/bg_rotate.jpg'); + + const sprite = new Sprite(texture); + + // // BEZIER CURVE //// + // information: https://en.wikipedia.org/wiki/Bézier_curve + + const realPath = new Graphics(); + + realPath.moveTo(0, 0); + realPath.lineTo(100, 200); + realPath.lineTo(200, 200); + realPath.lineTo(240, 100); + realPath.stroke({ width: 2, color: 0xffffff }); + + realPath.position.x = 50; + realPath.position.y = 50; + + app.stage.addChild(realPath); + + const bezier = new Graphics(); + + bezier.bezierCurveTo(100, 200, 200, 200, 240, 100); + bezier.stroke({ width: 5, color: 0xaa0000 }); + + bezier.position.x = 50; + bezier.position.y = 50; + + app.stage.addChild(bezier); + + // // BEZIER CURVE 2 //// + const realPath2 = new Graphics(); + + realPath2.moveTo(0, 0); + realPath2.lineTo(0, -100); + realPath2.lineTo(150, 150); + realPath2.lineTo(240, 100); + realPath2.stroke({ width: 2, color: 0xffffff }); + + realPath2.position.x = 320; + realPath2.position.y = 150; + + app.stage.addChild(realPath2); + + const bezier2 = new Graphics(); + + bezier2.bezierCurveTo(0, -100, 150, 150, 240, 100); + bezier2.stroke({ width: 10, texture: sprite.texture }); + + bezier2.position.x = 320; + bezier2.position.y = 150; + + app.stage.addChild(bezier2); + + // // ARC //// + const arc = new Graphics(); + + arc.arc(600, 100, 50, Math.PI, 2 * Math.PI); + arc.stroke({ width: 5, color: 0xaa00bb }); + + app.stage.addChild(arc); + + // // ARC 2 //// + const arc2 = new Graphics(); + + arc2.arc(650, 270, 60, 2 * Math.PI, (3 * Math.PI) / 2); + arc2.stroke({ width: 6, color: 0x3333dd }); + + app.stage.addChild(arc2); + + // // ARC 3 //// + const arc3 = new Graphics(); + + arc3.arc(650, 420, 60, 2 * Math.PI, (2.5 * Math.PI) / 2); + arc3.stroke({ width: 20, texture: sprite.texture }); + + app.stage.addChild(arc3); + + // / Hole //// + const rectAndHole = new Graphics(); + + rectAndHole.rect(350, 350, 150, 150); + rectAndHole.fill(0x00ff00); + rectAndHole.circle(375, 375, 25); + rectAndHole.circle(425, 425, 25); + rectAndHole.circle(475, 475, 25); + rectAndHole.cut(); + + app.stage.addChild(rectAndHole); + + // // Line Texture Style //// + const beatifulRect = new Graphics(); + + beatifulRect.rect(80, 350, 150, 150); + beatifulRect.fill(0xff0000); + beatifulRect.stroke({ width: 20, texture: sprite.texture }); + + app.stage.addChild(beatifulRect); +})(); diff --git a/src/examples/v8.0.0-rc.1/graphics/dynamic.js b/src/examples/v8.0.0-rc.1/graphics/dynamic.js new file mode 100644 index 000000000..02a0e87b6 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/graphics/dynamic.js @@ -0,0 +1,99 @@ +import { Application, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + + const graphics = new Graphics(); + + // Draw a shape + graphics + .moveTo(50, 50) + .lineTo(250, 50) + .lineTo(100, 100) + .lineTo(250, 220) + .lineTo(50, 220) + .lineTo(50, 50) + .fill({ color: 0xff3300 }) + .stroke({ width: 10, color: 0xffd900 }); + + // Draw a second shape + graphics + .moveTo(210, 300) + .lineTo(450, 320) + .lineTo(570, 350) + .quadraticCurveTo(600, 0, 480, 100) + .lineTo(330, 120) + .lineTo(410, 200) + .lineTo(210, 300) + .fill({ color: 0xff700b }) + .stroke({ width: 10, color: 0xff0000, alpha: 0.8 }); + + // Draw a rectangle + graphics.rect(50, 250, 100, 100); + graphics.stroke({ width: 2, color: 0x0000ff }); + + // Draw a circle + graphics.circle(470, 200, 100); + graphics.fill({ color: 0xffff0b, alpha: 0.5 }); + + graphics.moveTo(30, 30); + graphics.lineTo(600, 300); + graphics.stroke({ width: 20, color: 0x33ff00 }); + + app.stage.addChild(graphics); + + // Let's create a moving shape + const thing = new Graphics(); + + app.stage.addChild(thing); + thing.x = 800 / 2; + thing.y = 600 / 2; + + let count = 0; + + // Just click on the stage to draw random lines + window.app = app; + app.stage.on('pointerdown', () => + { + graphics.moveTo(Math.random() * 800, Math.random() * 600); + graphics.bezierCurveTo( + Math.random() * 800, + Math.random() * 600, + Math.random() * 800, + Math.random() * 600, + Math.random() * 800, + Math.random() * 600, + ); + graphics.stroke({ width: Math.random() * 30, color: Math.random() * 0xffffff }); + }); + + // Animate the moving shape + app.ticker.add(() => + { + count += 0.1; + + thing.clear(); + + thing + .moveTo(-120 + Math.sin(count) * 20, -100 + Math.cos(count) * 20) + .lineTo(120 + Math.cos(count) * 20, -100 + Math.sin(count) * 20) + .lineTo(120 + Math.sin(count) * 20, 100 + Math.cos(count) * 20) + .lineTo(-120 + Math.cos(count) * 20, 100 + Math.sin(count) * 20) + .lineTo(-120 + Math.sin(count) * 20, -100 + Math.cos(count) * 20) + .fill({ color: 0xffff00, alpha: 0.5 }) + .stroke({ width: 10, color: 0xff0000 }); + + thing.rotation = count * 0.1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/graphics/simple.js b/src/examples/v8.0.0-rc.1/graphics/simple.js new file mode 100644 index 000000000..aab81dd83 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/graphics/simple.js @@ -0,0 +1,89 @@ +import { Application, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const graphics = new Graphics(); + + // Rectangle + graphics.rect(50, 50, 100, 100); + graphics.fill(0xde3249); + + // Rectangle + line style 1 + graphics.rect(200, 50, 100, 100); + graphics.fill(0x650a5a); + graphics.stroke({ width: 2, color: 0xfeeb77 }); + + // Rectangle + line style 2 + graphics.rect(350, 50, 100, 100); + graphics.fill(0xc34288); + graphics.stroke({ width: 10, color: 0xffbd01 }); + + // Rectangle 2 + graphics.rect(530, 50, 140, 100); + graphics.fill(0xaa4f08); + graphics.stroke({ width: 2, color: 0xffffff }); + + // Circle + graphics.circle(100, 250, 50); + graphics.fill(0xde3249, 1); + + // Circle + line style 1 + graphics.circle(250, 250, 50); + graphics.fill(0x650a5a, 1); + graphics.stroke({ width: 2, color: 0xfeeb77 }); + + // Circle + line style 2 + graphics.circle(400, 250, 50); + graphics.fill(0xc34288, 1); + graphics.stroke({ width: 10, color: 0xffbd01 }); + + // Ellipse + line style 2 + graphics.ellipse(600, 250, 80, 50); + graphics.fill(0xaa4f08, 1); + graphics.stroke({ width: 2, color: 0xffffff }); + + // Draw a shape + graphics.moveTo(50, 350); + graphics.lineTo(250, 350); + graphics.lineTo(100, 400); + graphics.lineTo(50, 350); + graphics.fill(0xff3300); + graphics.stroke({ width: 4, color: 0xffd900 }); + + // Draw a rounded rectangle + graphics.roundRect(50, 440, 100, 100, 16); + graphics.fill(0x650a5a, 0.25); + graphics.stroke({ width: 2, color: 0xff00ff }); + + // Draw star + graphics.star(360, 370, 5, 50); + graphics.fill(0x35cc5a); + graphics.stroke({ width: 2, color: 0xffffff }); + + // Draw star 2 + graphics.star(280, 510, 7, 50); + graphics.fill(0xffcc5a); + graphics.stroke({ width: 2, color: 0xfffffd }); + + // Draw star 3 + graphics.star(470, 450, 4, 50); + graphics.fill(0x55335a); + graphics.stroke(4, 0xffffff); + + // Draw polygon + const path = [600, 370, 700, 460, 780, 420, 730, 570, 590, 520]; + + graphics.poly(path); + graphics.fill(0x3500fa); + + app.stage.addChild(graphics); +})(); diff --git a/src/examples/v8.0.0-rc.1/index.ts b/src/examples/v8.0.0-rc.1/index.ts new file mode 100644 index 000000000..9ccb7eb3c --- /dev/null +++ b/src/examples/v8.0.0-rc.1/index.ts @@ -0,0 +1,255 @@ +import collisionDetection from '!!raw-loader!./advanced/collisionDetection.js'; +import mouseTrail from '!!raw-loader!./advanced/mouseTrail.js'; +import scratchCard from '!!raw-loader!./advanced/scratchCard.js'; +import screenShot from '!!raw-loader!./advanced/screenShot.js'; +import slots from '!!raw-loader!./advanced/slots.js'; +import spinners from '!!raw-loader!./advanced/spinners.js'; +import starWarp from '!!raw-loader!./advanced/starWarp.js'; + +import async from '!!raw-loader!./assets/async.js'; +import background from '!!raw-loader!./assets/background.js'; +import bundle from '!!raw-loader!./assets/bundle.js'; +import multiple from '!!raw-loader!./assets/multiple.js'; +import promise from '!!raw-loader!./assets/promise.js'; + +import blendModes from '!!raw-loader!./basic/blendModes.js'; +import container from '!!raw-loader!./basic/container.js'; +import particleContainer from '!!raw-loader!./basic/particleContainer.js'; +import meshPlane from '!!raw-loader!./basic/meshPlane.js'; +import tinting from '!!raw-loader!./basic/tinting.js'; +import transparentBackground from '!!raw-loader!./basic/transparentBackground.js'; +import fillGradient from '!!raw-loader!./basic/fillGradient.js'; + +import click from '!!raw-loader!./events/click.js'; +import customHitarea from '!!raw-loader!./events/customHitarea.js'; +import customMouseIcon from '!!raw-loader!./events/customMouseIcon.js'; +import dragging from '!!raw-loader!./events/dragging.js'; +import hitTestingWithSpatialHash from '!!raw-loader!./events/hitTestingWithSpatialHash.js'; +import interactivity from '!!raw-loader!./events/interactivity.js'; +import logger from '!!raw-loader!./events/logger.js'; +import nestedBoundaryWithProjection from '!!raw-loader!./events/nestedBoundaryWithProjection.js'; +import pointerTracker from '!!raw-loader!./events/pointerTracker.js'; +import slider from '!!raw-loader!./events/slider.js'; + +import custom from '!!raw-loader!./filtersAdvanced/custom.js'; +import mouseBlending from '!!raw-loader!./filtersAdvanced/mouseBlending.js'; +import shaderToyFilterRenderTexture from '!!raw-loader!./filtersAdvanced/shaderToyFilterRenderTexture.js'; + +import blur from '!!raw-loader!./filtersBasic/blur.js'; +import colorMatrix from '!!raw-loader!./filtersBasic/colorMatrix.js'; +import displacementMapCrawlies from '!!raw-loader!./filtersBasic/displacementMapCrawlies.js'; +import displacementMapFlag from '!!raw-loader!./filtersBasic/displacementMapFlag.js'; + +import advanced from '!!raw-loader!./graphics/advanced.js'; +import dynamic from '!!raw-loader!./graphics/dynamic.js'; +import simple from '!!raw-loader!./graphics/simple.js'; + +import filter from '!!raw-loader!./masks/filter.js'; +import graphics from '!!raw-loader!./masks/graphics.js'; +import sprite from '!!raw-loader!./masks/sprite.js'; + +import instancedGeometry from '!!raw-loader!./meshAndShaders/instancedGeometry.js'; +import mergingGeometry from '!!raw-loader!./meshAndShaders/mergingGeometry.js'; +import multiPassShaderGeneratedMesh from '!!raw-loader!./meshAndShaders/multiPassShaderGeneratedMesh.js'; +import shaderToyMesh from '!!raw-loader!./meshAndShaders/shaderToyMesh.js'; +import sharedShader from '!!raw-loader!./meshAndShaders/sharedShader.js'; +import sharingGeometry from '!!raw-loader!./meshAndShaders/sharingGeometry.js'; +import texturedMeshAdvanced from '!!raw-loader!./meshAndShaders/texturedMeshAdvanced.js'; +import texturedMeshBasic from '!!raw-loader!./meshAndShaders/texturedMeshBasic.js'; +import triangleColor from '!!raw-loader!./meshAndShaders/triangleColor.js'; +import triangleTextured from '!!raw-loader!./meshAndShaders/triangleTextured.js'; +import triangle from '!!raw-loader!./meshAndShaders/triangle.js'; +import uniforms from '!!raw-loader!./meshAndShaders/uniforms.js'; + +import offscreenCanvasBasic from '!!raw-loader!./offscreenCanvas/basic.js'; +import webWorker from '!!raw-loader!./offscreenCanvas/webWorker.js'; + +import animatedSpriteAnimationSpeed from '!!raw-loader!./sprite/animatedSpriteAnimationSpeed.js'; +import animatedSpriteExplosion from '!!raw-loader!./sprite/animatedSpriteExplosion.js'; +import animatedSpriteJet from '!!raw-loader!./sprite/animatedSpriteJet.js'; +import spriteBasic from '!!raw-loader!./sprite/basic.js'; +import textureSwap from '!!raw-loader!./sprite/textureSwap.js'; +import tilingSprite from '!!raw-loader!./sprite/tilingSprite.js'; +import video from '!!raw-loader!./sprite/video.js'; + +import bitmapText from '!!raw-loader!./text/bitmapText.js'; +import fromFont from '!!raw-loader!./text/fromFont.js'; +import pixiText from '!!raw-loader!./text/pixiText.js'; +import webFont from '!!raw-loader!./text/webFont.js'; + +import renderTextureAdvanced from '!!raw-loader!./textures/renderTextureAdvanced.js'; +import renderTextureBasic from '!!raw-loader!./textures/renderTextureBasic.js'; +import textureRotate from '!!raw-loader!./textures/textureRotate.js'; +import type { Option } from '@site/src/components/Select'; + +// Defines order of examples in documentation and playground dropdown, it's defined +// separately here so it can be used in runtime code and in the md generation script +import examplesData from './examplesData.json'; +import { camelCaseToSentenceCase } from '@site/src/utils/utils'; +import type { CategoryDataType, ExampleDataEntry, ExamplesDataType, ExamplesJsonType, ExamplesSourceType } from '..'; + +const examplesSource: ExamplesSourceType = { + basic: { + blendModes, + container, + particleContainer, + meshPlane, + tinting, + transparentBackground, + fillGradient, + }, + sprite: { + animatedSpriteAnimationSpeed, + animatedSpriteExplosion, + animatedSpriteJet, + basic: spriteBasic, + textureSwap, + tilingSprite, + video, + }, + text: { + bitmapText, + fromFont, + pixiText, + webFont, + }, + graphics: { + advanced, + dynamic, + simple, + }, + events: { + click, + customHitarea, + customMouseIcon, + dragging, + hitTestingWithSpatialHash, + interactivity, + logger, + nestedBoundaryWithProjection, + pointerTracker, + slider, + }, + masks: { + filter, + graphics, + sprite, + }, + textures: { + renderTextureAdvanced, + renderTextureBasic, + textureRotate, + }, + assets: { + async, + background, + bundle, + multiple, + promise, + }, + offscreenCanvas: { + basic: offscreenCanvasBasic, + webWorker, + }, + filtersBasic: { + blur, + colorMatrix, + displacementMapCrawlies, + displacementMapFlag, + }, + filtersAdvanced: { + custom, + mouseBlending, + shaderToyFilterRenderTexture, + }, + advanced: { + collisionDetection, + mouseTrail, + scratchCard, + screenShot, + slots, + spinners, + starWarp, + }, + meshAndShaders: { + instancedGeometry, + mergingGeometry, + multiPassShaderGeneratedMesh, + shaderToyMesh, + sharedShader, + sharingGeometry, + texturedMeshAdvanced, + texturedMeshBasic, + triangleColor, + triangleTextured, + triangle, + uniforms, + }, +}; + +const normalizeExampleDataEntry = (categoryExample: ExampleDataEntry | string): Required => +{ + const defaults = { + hide: false, + usesWebWorkerLibrary: false, + }; + + if (typeof categoryExample === 'string') + { + return { + ...defaults, + name: categoryExample, + }; + } + + return { + ...defaults, + ...categoryExample, + }; +}; + +const entries = Object.entries(examplesData as ExamplesJsonType).reduce( + (directoryAcc, [categoryKey, categoryExamples]) => ({ + ...directoryAcc, + [categoryKey]: categoryExamples.reduce((categoryAcc, categoryExampleOrString) => + { + const categoryExample = normalizeExampleDataEntry(categoryExampleOrString); + const { name: categoryName, hide, usesWebWorkerLibrary } = categoryExample; + + return { + ...categoryAcc, + [categoryName]: { + source: examplesSource[categoryKey][categoryName], + hide, + usesWebWorkerLibrary, + }, + }; + }, {} as CategoryDataType), + }), + {} as ExamplesDataType, +); + +const options = Object.entries(examplesData as ExamplesJsonType).map(([folderKey, folderEntries]) => +{ + const options = folderEntries.reduce((acc, exampleDataEntry) => + { + const { name: exampleKey, hide } = normalizeExampleDataEntry(exampleDataEntry); + + if (hide) + { + return acc; + } + + return acc.concat({ + value: `${folderKey}.${exampleKey}`, + label: camelCaseToSentenceCase(exampleKey), + }); + }, [] as Option[]); + + return { + label: camelCaseToSentenceCase(folderKey), + options, + }; +}); + +export default { entries, options }; diff --git a/src/examples/v8.0.0-rc.1/masks/filter.js b/src/examples/v8.0.0-rc.1/masks/filter.js new file mode 100644 index 000000000..ad68a24ea --- /dev/null +++ b/src/examples/v8.0.0-rc.1/masks/filter.js @@ -0,0 +1,54 @@ +import { Application, Assets, Sprite, Graphics, SCALE_MODES, Rectangle, BlurFilter } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Inner radius of the circle + const radius = 100; + + // The blur amount + const blurSize = 32; + + // Load the grass texture + const grassTexture = await Assets.load('https://pixijs.com/assets/bg_grass.jpg'); + + // Create the grass background + const background = new Sprite(grassTexture); + + app.stage.addChild(background); + background.width = app.screen.width; + background.height = app.screen.height; + + const circle = new Graphics().circle(radius + blurSize, radius + blurSize, radius).fill({ color: 0xff0000 }); + + circle.filters = [new BlurFilter(blurSize)]; + + const bounds = new Rectangle(0, 0, (radius + blurSize) * 2, (radius + blurSize) * 2); + const texture = app.renderer.generateTexture({ + target: circle, + style: { scaleMode: SCALE_MODES.NEAREST }, + resolution: 1, + frame: bounds, + }); + const focus = new Sprite(texture); + + app.stage.addChild(focus); + + background.mask = focus; + + app.stage.eventMode = 'static'; + app.stage.hitArea = app.screen; + app.stage.on('pointermove', (event) => + { + focus.position.x = event.global.x - focus.width / 2; + focus.position.y = event.global.y - focus.height / 2; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/masks/graphics.js b/src/examples/v8.0.0-rc.1/masks/graphics.js new file mode 100644 index 000000000..40b898d3c --- /dev/null +++ b/src/examples/v8.0.0-rc.1/masks/graphics.js @@ -0,0 +1,119 @@ +import { Application, Assets, Container, Sprite, Graphics, Text } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ antialias: true, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + app.stage.eventMode = 'static'; + + // Load textures + await Assets.load([ + 'https://pixijs.com/assets/bg_rotate.jpg', + 'https://pixijs.com/assets/bg_scene_rotate.jpg', + 'https://pixijs.com/assets/light_rotate_2.png', + 'https://pixijs.com/assets/light_rotate_1.png', + 'https://pixijs.com/assets/panda.png', + ]); + + const bg = Sprite.from('https://pixijs.com/assets/bg_rotate.jpg'); + + bg.anchor.set(0.5); + + bg.x = app.screen.width / 2; + bg.y = app.screen.height / 2; + + app.stage.addChild(bg); + + const container = new Container(); + + container.x = app.screen.width / 2; + container.y = app.screen.height / 2; + + // Add a bunch of sprites + const bgFront = Sprite.from('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + bgFront.anchor.set(0.5); + + const light2 = Sprite.from('https://pixijs.com/assets/light_rotate_2.png'); + + light2.anchor.set(0.5); + + const light1 = Sprite.from('https://pixijs.com/assets/light_rotate_1.png'); + + light1.anchor.set(0.5); + + const panda = Sprite.from('https://pixijs.com/assets/panda.png'); + + panda.anchor.set(0.5); + + container.addChild(bgFront, light2, light1, panda); + + app.stage.addChild(container); + + // Let's create a moving shape mask + const thing = new Graphics(); + + app.stage.addChild(thing); + thing.x = app.screen.width / 2; + thing.y = app.screen.height / 2; + + container.mask = thing; + + let count = 0; + + app.stage.on('pointertap', () => + { + if (!container.mask) + { + container.mask = thing; + } + else + { + container.mask = null; + } + }); + + const help = new Text({ + text: 'Click or tap to turn masking on / off.', + style: { + fontFamily: 'Arial', + fontSize: 12, + fontWeight: 'bold', + fill: 'white', + }, + }); + + help.y = app.screen.height - 26; + help.x = 10; + app.stage.addChild(help); + + // Animate the mask + app.ticker.add(() => + { + bg.rotation += 0.01; + bgFront.rotation -= 0.01; + + light1.rotation += 0.02; + light2.rotation += 0.01; + + panda.scale.x = 1 + Math.sin(count) * 0.04; + panda.scale.y = 1 + Math.cos(count) * 0.04; + + count += 0.1; + + thing.clear(); + thing.moveTo(-120 + Math.sin(count) * 20, -100 + Math.cos(count) * 20); + thing.lineTo(120 + Math.cos(count) * 20, -100 + Math.sin(count) * 20); + thing.lineTo(120 + Math.sin(count) * 20, 100 + Math.cos(count) * 20); + thing.lineTo(-120 + Math.cos(count) * 20, 100 + Math.sin(count) * 20); + thing.fill({ color: 0x8bc5ff, alpha: 0.4 }); + thing.rotation = count * 0.1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/masks/sprite.js b/src/examples/v8.0.0-rc.1/masks/sprite.js new file mode 100644 index 000000000..41d6dd6eb --- /dev/null +++ b/src/examples/v8.0.0-rc.1/masks/sprite.js @@ -0,0 +1,62 @@ +import { Application, Assets, Sprite, Point } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load textures + await Assets.load([ + 'https://pixijs.com/assets/bg_plane.jpg', + 'https://pixijs.com/assets/cells.png', + 'https://pixijs.com/assets/flowerTop.png', + ]); + + app.stage.eventMode = 'static'; + + const bg = Sprite.from('https://pixijs.com/assets/bg_plane.jpg'); + + app.stage.addChild(bg); + + const cells = Sprite.from('https://pixijs.com/assets/cells.png'); + + cells.scale.set(1.5); + + const mask = Sprite.from('https://pixijs.com/assets/flowerTop.png'); + + mask.anchor.set(0.5); + mask.x = 310; + mask.y = 190; + + cells.mask = mask; + + app.stage.addChild(mask, cells); + + const target = new Point(); + + reset(); + + function reset() + { + target.x = Math.floor(Math.random() * 550); + target.y = Math.floor(Math.random() * 300); + } + + // Animate the mask + app.ticker.add(() => + { + mask.x += (target.x - mask.x) * 0.1; + mask.y += (target.y - mask.y) * 0.1; + + if (Math.abs(mask.x - target.x) < 1) + { + reset(); + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/instancedGeometry.js b/src/examples/v8.0.0-rc.1/meshAndShaders/instancedGeometry.js new file mode 100644 index 000000000..2f6fcb785 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/instancedGeometry.js @@ -0,0 +1,84 @@ +import { Application, Assets, Mesh, Geometry, Buffer, TYPES, Shader } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const geometry = new Geometry().addAttribute('aVPos', [-100, 0, 100, 0, 0, -150]); + + geometry.instanced = true; + geometry.instanceCount = 5; + + const positionSize = 2; + const colorSize = 3; + const buffer = new Buffer(new Float32Array(geometry.instanceCount * (positionSize + colorSize))); + + geometry.addAttribute('aIPos', buffer, positionSize, false, TYPES.FLOAT, 4 * (positionSize + colorSize), 0, true); + geometry.addAttribute( + 'aICol', + buffer, + colorSize, + false, + TYPES.FLOAT, + 4 * (positionSize + colorSize), + 4 * positionSize, + true, + ); + + for (let i = 0; i < geometry.instanceCount; i++) + { + const instanceOffset = i * (positionSize + colorSize); + + buffer.data[instanceOffset + 0] = i * 80; + buffer.data[instanceOffset + 2] = Math.random(); + buffer.data[instanceOffset + 3] = Math.random(); + buffer.data[instanceOffset + 4] = Math.random(); + } + + const shader = Shader.from( + ` + precision mediump float; + attribute vec2 aVPos; + attribute vec2 aIPos; + attribute vec3 aICol; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec3 vCol; + + void main() { + vCol = aICol; + + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVPos + aIPos, 1.0)).xy, 0.0, 1.0); + }`, + + `precision mediump float; + + varying vec3 vCol; + + void main() { + gl_FragColor = vec4(vCol, 1.0); + } + +`, + ); + + const triangles = new Mesh(geometry, shader); + + triangles.position.set(400, 300); + + app.stage.addChild(triangles); + + app.ticker.add(() => + { + triangles.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/mergingGeometry.js b/src/examples/v8.0.0-rc.1/meshAndShaders/mergingGeometry.js new file mode 100644 index 000000000..fbfc5e04f --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/mergingGeometry.js @@ -0,0 +1,83 @@ +import { Application, Assets, Mesh, Geometry, Shader } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -100, 100, -100, 100, 100, -100, 100], + aUvs: [0, 0, 1, 0, 1, 1, 0, 1], + }, + indexBuffer: [0, 1, 2, 0, 2, 3], + }); + + const geometry2 = new Geometry({ + attributes: { + aVertexPosition: [-100 + 100, -100, 100 + 100, -100, 100 + 100, 100], + aUvs: [0, 0, 1, 0, 1, 1], + }, + indexBuffer: [0, 1, 2], + }); + + const geometry3 = Geometry.merge([geometry, geometry2]); + + const shader = Shader.from( + ` + + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + + void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`, + + `precision mediump float; + + varying vec2 vUvs; + + uniform sampler2D uSampler2; + + void main() { + + gl_FragColor = texture2D(uSampler2, vUvs ); + } + +`, + { + uSampler2: texture, + }, + ); + + const quad = new Mesh(geometry3, shader); + + quad.position.set(400, 300); + quad.scale.set(2); + + app.stage.addChild(quad); + + app.ticker.add((delta) => + { + quad.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/multiPassShaderGeneratedMesh.js b/src/examples/v8.0.0-rc.1/meshAndShaders/multiPassShaderGeneratedMesh.js new file mode 100644 index 000000000..3dfe67a4c --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/multiPassShaderGeneratedMesh.js @@ -0,0 +1,243 @@ +import { Application, Assets, Container, Mesh, Geometry, Texture, Shader, RenderTexture } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ height: 640, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Build geometry. + const geometry = new Geometry() + .addAttribute( + 'aVertexPosition', // the attribute name + [ + 0, + 0, // x, y + 200, + 0, // x, y + 200, + 200, + 0, + 200, + ], // x, y + 2, + ) // the size of the attribute + .addAttribute( + 'aUvs', // the attribute name + [ + 0, + 0, // u, v + 1, + 0, // u, v + 1, + 1, + 0, + 1, + ], // u, v + 2, + ) // the size of the attribute + .addIndex([0, 1, 2, 0, 2, 3]); + + // Vertex shader. Use same shader for all passes. + const vertexSrc = ` + +precision mediump float; + +attribute vec2 aVertexPosition; +attribute vec2 aUvs; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vUvs; + +void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + +}`; + + // Load a perlinnoise texture for one of the shaders. + const perlinTexture = Texture.from('https://pixijs.com/assets/perlin.jpg'); + + // First pass, generates a grid. + const fragmentGridSrc = ` +precision mediump float; +varying vec2 vUvs; +uniform float zoom; + +void main() +{ +//Generate a simple grid. +//Offset uv so that center is 0,0 and edges are -1,1 +vec2 uv = (vUvs-vec2(0.5))*2.0; +vec2 gUv = floor(uv*zoom); +vec4 color1 = vec4(0.8, 0.8, 0.8, 1.0); +vec4 color2 = vec4(0.4, 0.4, 0.4, 1.0); +vec4 outColor = mod(gUv.x + gUv.y, 2.) < 0.5 ? color1 : color2; +gl_FragColor = outColor; + +}`; + + const gridUniforms = { + zoom: 10, + }; + const gridShader = Shader.from(vertexSrc, fragmentGridSrc, gridUniforms); + // Sharing textures and meshes is possible. + // But for simplicity each pass has its own output texture and mesh in this example. + const gridTexture = RenderTexture.create({ width: 200, height: 200 }); + const gridQuad = new Mesh(geometry, gridShader); + const gridContainer = new Container(); + + gridContainer.addChild(gridQuad); + + // Second pass. Takes grid as input and makes it ripple. + const fragmentRippleSrc = ` +precision mediump float; +varying vec2 vUvs; +uniform float amount; +uniform float phase; +uniform sampler2D texIn; + +void main() +{ +//Generate a simple grid. +vec2 uv = vUvs; +//Calculate distance from center +float distance = length( uv - vec2(0.5)); +vec4 color = texture2D(texIn, uv); +color.rgb *= sin(distance*25.0+phase) * amount+1.; +gl_FragColor = color; +}`; + const rippleUniforms = { + amount: 0.5, + phase: 0, + texIn: gridTexture, + }; + const rippleShader = Shader.from(vertexSrc, fragmentRippleSrc, rippleUniforms); + const rippleTexture = RenderTexture.create({ width: 200, height: 200 }); + const rippleQuad = new Mesh(geometry, rippleShader); + const rippleContainer = new Container(); + + rippleContainer.addChild(rippleQuad); + + // Second effect. Generates a filtered noise. + const fragmentNoiseSrc = ` +precision mediump float; +varying vec2 vUvs; +uniform float limit; +uniform sampler2D noise; + +void main() +{ +float color = texture2D(noise, vUvs).r; +color = step(limit, color); +gl_FragColor = vec4(color); +}`; + const noiseUniforms = { + limit: 0.5, + noise: perlinTexture, + }; + const noiseShader = Shader.from(vertexSrc, fragmentNoiseSrc, noiseUniforms); + const noiseTexture = RenderTexture.create({ width: 200, height: 200 }); + const noiseQuad = new Mesh(geometry, noiseShader); + const noiseContainer = new Container(); + + noiseContainer.addChild(noiseQuad); + + // Third effect + const fragmentWaveSrc = ` +precision mediump float; +varying vec2 vUvs; +uniform float amplitude; +uniform float time; + +void main() +{ +//Offset uv so that center is 0,0 and edges are -1,1 +vec2 uv = (vUvs-vec2(0.5))*2.0; + +vec3 outColor = vec3(0.); + +//Simple wavefunctions inversed and with small offsets. +outColor += 5./length(uv.y*200. - 50.0*sin( uv.x*0.25+ time*0.25)*amplitude); +outColor += 4./length(uv.y*300. - 100.0*sin(uv.x*0.5+time*0.5)*amplitude*1.2); +outColor += 3./length(uv.y*400. - 150.0*sin(uv.x*0.75+time*0.75)*amplitude*1.4); +outColor += 2./length(uv.y*500. - 200.0*sin(uv.x+time)*amplitude*1.6); + +gl_FragColor = vec4(outColor,1.0); +}`; + const waveUniforms = { + amplitude: 0.75, + time: 0, + }; + const waveShader = Shader.from(vertexSrc, fragmentWaveSrc, waveUniforms); + const waveTexture = RenderTexture.create({ width: 200, height: 200 }); + const waveQuad = new Mesh(geometry, waveShader); + const waveContainer = new Container(); + + waveContainer.addChild(waveQuad); + + // Final combination pass + const fragmentCombineSrc = ` +precision mediump float; +varying vec2 vUvs; + +uniform sampler2D texRipple; +uniform sampler2D texNoise; +uniform sampler2D texWave; + +void main() +{ +//Read color from all +vec4 ripple = texture2D(texRipple, vUvs); +vec4 noise = texture2D(texNoise, vUvs); +vec4 wave = texture2D(texWave, vUvs); + +gl_FragColor = mix(ripple, wave,noise.r); +}`; + const combineUniforms = { + texRipple: rippleTexture, + texNoise: noiseTexture, + texWave: waveTexture, + }; + const combineShader = Shader.from(vertexSrc, fragmentCombineSrc, combineUniforms); + const combineQuad = new Mesh(geometry, combineShader); + + gridContainer.position.set(10, 10); + rippleContainer.position.set(220, 10); + noiseContainer.position.set(10, 220); + waveContainer.position.set(10, 430); + combineQuad.position.set(430, 220); + + // Add all phases to stage so all the phases can be seen separately. + app.stage.addChild(gridContainer); + app.stage.addChild(rippleContainer); + app.stage.addChild(noiseContainer); + app.stage.addChild(waveContainer); + app.stage.addChild(combineQuad); + + // start the animation.. + let time = 0; + + app.ticker.add(() => + { + time += 1 / 60; + // gridQuad.shader.uniforms.zoom = Math.sin(time)*5+10; + rippleQuad.shader.uniforms.phase = -time; + waveQuad.shader.uniforms.time = time; + noiseQuad.shader.uniforms.limit = Math.sin(time * 0.5) * 0.35 + 0.5; + + // Render the passes to get textures. + app.renderer.render(gridQuad, { renderTexture: gridTexture }); + app.renderer.render(rippleQuad, { renderTexture: rippleTexture }); + app.renderer.render(noiseQuad, { renderTexture: noiseTexture }); + app.renderer.render(waveQuad, { renderTexture: waveTexture }); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/shaderToyMesh.js b/src/examples/v8.0.0-rc.1/meshAndShaders/shaderToyMesh.js new file mode 100644 index 000000000..372c40af7 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/shaderToyMesh.js @@ -0,0 +1,166 @@ +import { Application, Assets, Mesh, Shader, WRAP_MODES, Geometry } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/perlin.jpg'); + + // Build geometry. + const geometry = new Geometry() + .addAttribute( + 'aVertexPosition', // the attribute name + [ + -100, + -100, // x, y + 100, + -100, // x, y + 100, + 100, + -100, + 100, + ], // x, y + 2, + ) // the size of the attribute + .addAttribute( + 'aUvs', // the attribute name + [ + 0, + 0, // u, v + 1, + 0, // u, v + 1, + 1, + 0, + 1, + ], // u, v + 2, + ) // the size of the attribute + .addIndex([0, 1, 2, 0, 2, 3]); + + const vertexSrc = ` + +precision mediump float; + +attribute vec2 aVertexPosition; +attribute vec2 aUvs; + +uniform mat3 translationMatrix; +uniform mat3 projectionMatrix; + +varying vec2 vUvs; + +void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + +}`; + + const fragmentSrc = ` +//Based on this: https://www.shadertoy.com/view/wtlSWX +precision mediump float; + +varying vec2 vUvs; + +uniform sampler2D noise; +uniform float time; + +// Distance function. Just calculates the height (z) from x,y plane with really simple length check. +// Its not exact as there could be shorter distances. +vec2 dist(vec3 p) +{ +float id = floor(p.x)+floor(p.y); +id = mod(id, 2.); +float h = texture2D(noise, vec2(p.x, p.y)*0.04).r*5.1; +return vec2(h-p.z,id); +} + +//Light calculation. +vec3 calclight(vec3 p, vec3 rd) +{ +vec2 eps = vec2( 0., 0.001); +vec3 n = normalize( vec3( +dist(p+eps.yxx).x - dist(p-eps.yxx).x, +dist(p+eps.xyx).x - dist(p-eps.xyx).x, +dist(p+eps.xxy).x - dist(p-eps.xxy).x +)); + +vec3 d = vec3( max( 0., dot( -rd ,n))); + +return d; +} + +void main() +{ +vec2 uv = vec2(vUvs.x,1.-vUvs.y); +uv *=2.; +uv-=1.; + +vec3 cam = vec3(0.,time -2., -3.); +vec3 target = vec3(sin(time)*0.1, time+cos(time)+2., 0. ); +float fov = 2.2; +vec3 forward = normalize( target - cam); +vec3 up = normalize(cross( forward, vec3(0., 1.,0.))); +vec3 right = normalize( cross( up, forward)); +vec3 raydir = normalize(vec3( uv.x *up + uv.y * right + fov*forward)); + +//Do the raymarch +vec3 col = vec3(0.); +float t = 0.; +for( int i = 0; i < 100; i++) +{ +vec3 p = t * raydir + cam; +vec2 d = dist(p); +t+=d.x*0.5;//Jump only half of the distance as height function used is not really the best for heightmaps. +if(d.x < 0.001) +{ + vec3 bc = d.y < 0.5 ? vec3(1.0, .8, 0.) : + vec3(0.8,0.0, 1.0); + col = vec3( 1.) * calclight(p, raydir) * (1. - t/150.) *bc; + break; +} +if(t > 1000.) +{ + break; +} +} +gl_FragColor = vec4(col, 1.); +}`; + + const uniforms = { + noise: texture, + time: 0, + }; + // Make sure repeat wrap is used and no mipmapping. + + uniforms.noise.baseTexture.wrapMode = WRAP_MODES.REPEAT; + uniforms.noise.baseTexture.mipmap = false; + + // Build the shader and the quad. + const shader = Shader.from(vertexSrc, fragmentSrc, uniforms); + const quad = new Mesh(geometry, shader); + + quad.position.set(400, 300); + quad.scale.set(2); + + app.stage.addChild(quad); + + // start the animation.. + let time = 0; + + app.ticker.add(() => + { + time += 1 / 60; + quad.shader.uniforms.time = time; + quad.scale.set(Number(Math.cos(time)) + 2, Number(Math.sin(time * 0.7)) + 2); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/sharedShader.js b/src/examples/v8.0.0-rc.1/meshAndShaders/sharedShader.js new file mode 100644 index 000000000..9da1df82a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/sharedShader.js @@ -0,0 +1,117 @@ +import { Application, Assets, Geometry, Shader, Mesh } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -100, 100, -100, 100, 100], + aUvs: [0, 0, 1, 0, 1, 1], + }, + }); + + const shader = Shader.from( + ` + + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + + void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`, + + `precision mediump float; + + varying vec2 vUvs; + + uniform sampler2D uSampler2; + + void main() { + + gl_FragColor = texture2D(uSampler2, vUvs); + } + +`, + { + uSampler2: texture, + }, + ); + + const shader2 = Shader.from( + ` + + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + + void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`, + + `precision mediump float; + + varying vec2 vUvs; + + uniform sampler2D uSampler2; + + void main() { + + gl_FragColor = texture2D(uSampler2, vUvs); + gl_FragColor.r += (abs(sin(gl_FragCoord.x * 0.06)) * 0.5) * 2.; + gl_FragColor.g += (abs(cos(gl_FragCoord.y * 0.06)) * 0.5) * 2.; + } + +`, + { + uSampler2: texture, + }, + ); + + const triangle = new Mesh(geometry, shader); + + const triangle2 = new Mesh(geometry, shader2); + + triangle.position.set(400, 300); + triangle.scale.set(2); + + triangle2.position.set(500, 400); + triangle2.scale.set(3); + + app.stage.addChild(triangle2, triangle); + + app.ticker.add((delta) => + { + triangle.rotation += 0.01; + triangle2.rotation -= 0.005; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/sharingGeometry.js b/src/examples/v8.0.0-rc.1/meshAndShaders/sharingGeometry.js new file mode 100644 index 000000000..efef94235 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/sharingGeometry.js @@ -0,0 +1,98 @@ +import { Application, Assets, Geometry, Texture, Mesh, Shader, Program } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + await Assets.load([ + 'https://pixijs.com/assets/bg_scene_rotate.jpg', + 'https://pixijs.com/assets/bg_rotate.jpg', + 'https://pixijs.com/assets/bg_displacement.jpg', + ]); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -100, 100, -100, 100, 100], + aUvs: [0, 0, 1, 0, 1, 1], + }, + }); + + const program = Program.from( + ` + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + + void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`, + + `precision mediump float; + + varying vec2 vUvs; + + uniform sampler2D uSamplerTexture; + + void main() { + + gl_FragColor = texture2D(uSamplerTexture, vUvs); + } + +`, + ); + + const triangle = new Mesh( + geometry, + new Shader(program, { + uSamplerTexture: Texture.from('https://pixijs.com/assets/bg_scene_rotate.jpg'), + }), + ); + + const triangle2 = new Mesh( + geometry, + new Shader(program, { + uSamplerTexture: Texture.from('https://pixijs.com/assets/bg_rotate.jpg'), + }), + ); + + const triangle3 = new Mesh( + geometry, + new Shader(program, { + uSamplerTexture: Texture.from('https://pixijs.com/assets/bg_displacement.jpg'), + }), + ); + + triangle.position.set(400, 300); + triangle.scale.set(2); + + triangle2.position.set(200, 100); + + triangle3.position.set(500, 400); + triangle3.scale.set(3); + + app.stage.addChild(triangle3, triangle2, triangle); + + app.ticker.add((delta) => + { + triangle.rotation += 0.01; + triangle2.rotation -= 0.01; + triangle3.rotation -= 0.005; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshAdvanced.js b/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshAdvanced.js new file mode 100644 index 000000000..17795d33e --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshAdvanced.js @@ -0,0 +1,75 @@ +import { Application, Assets, Point, MeshRope, Graphics } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the snake texture + const texture = await Assets.load('https://pixijs.com/assets/snake.png'); + + let count = 0; + + // Build a rope from points! + const ropeLength = 45; + + const points = []; + + for (let i = 0; i < 25; i++) + { + points.push(new Point(i * ropeLength, 0)); + } + + // Create the snake MeshRope + const strip = new MeshRope({ texture, points }); + + strip.x = -40; + strip.y = 300; + + app.stage.addChild(strip); + + const g = new Graphics(); + + g.x = strip.x; + g.y = strip.y; + app.stage.addChild(g); + + // Start animating + app.ticker.add(() => + { + count += 0.1; + + // Make the snake + for (let i = 0; i < points.length; i++) + { + points[i].y = Math.sin(i * 0.5 + count) * 30; + points[i].x = i * ropeLength + Math.cos(i * 0.3 + count) * 20; + } + renderPoints(); + }); + + function renderPoints() + { + g.clear(); + g.moveTo(points[0].x, points[0].y); + + for (let i = 1; i < points.length; i++) + { + g.lineTo(points[i].x, points[i].y); + g.stroke({ width: 2, color: 0xffc2c2 }); + } + + for (let i = 1; i < points.length; i++) + { + g.drawCircle(points[i].x, points[i].y, 10); + g.fill({ color: 0xff0022 }); + g.stroke({ width: 2, color: 0xffc2c2 }); + } + } +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshBasic.js b/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshBasic.js new file mode 100644 index 000000000..5ca550c06 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/texturedMeshBasic.js @@ -0,0 +1,55 @@ +import { Application, Assets, Point, Container, MeshRope } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the snake texture + const texture = await Assets.load('https://pixijs.com/assets/snake.png'); + + let count = 0; + + // Build a rope from points! + const ropeLength = 918 / 20; + const points = []; + + for (let i = 0; i < 20; i++) + { + points.push(new Point(i * ropeLength, 0)); + } + + // Create the snake MeshRope + const strip = new MeshRope({ texture, points }); + + strip.x = -459; + + const snakeContainer = new Container(); + + snakeContainer.x = 400; + snakeContainer.y = 300; + + snakeContainer.scale.set(800 / 1100); + app.stage.addChild(snakeContainer); + + snakeContainer.addChild(strip); + + // Animate the rope points + app.ticker.add(() => + { + count += 0.1; + + // make the snake + for (let i = 0; i < points.length; i++) + { + points[i].y = Math.sin(i * 0.5 + count) * 30; + points[i].x = i * ropeLength + Math.cos(i * 0.3 + count) * 20; + } + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/triangle.js b/src/examples/v8.0.0-rc.1/meshAndShaders/triangle.js new file mode 100644 index 000000000..bdcacf75a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/triangle.js @@ -0,0 +1,47 @@ +import { Application, Geometry, Shader, Mesh } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const geometry = new Geometry({ attribute: { aVertexPosition: [-100, -50, 100, -50, 0, 100] } }); + + const shader = Shader.from( + ` + precision mediump float; + attribute vec2 aVertexPosition; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + void main() { + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + }`, + + `precision mediump float; + + void main() { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + + `, + ); + + const triangle = new Mesh(geometry, shader); + + triangle.position.set(400, 300); + + app.stage.addChild(triangle); + + app.ticker.add((delta) => + { + triangle.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/triangleColor.js b/src/examples/v8.0.0-rc.1/meshAndShaders/triangleColor.js new file mode 100644 index 000000000..cb7f02d21 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/triangleColor.js @@ -0,0 +1,60 @@ +import { Application, Shader, Mesh, Geometry } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -50, 100, -50, 0, 100], + aColor: [1, 0, 0, 0, 1, 0, 0, 0, 1], + }, + }); + + const shader = Shader.from( + ` + precision mediump float; + attribute vec2 aVertexPosition; + attribute vec3 aColor; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec3 vColor; + + void main() { + + vColor = aColor; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`, + `precision mediump float; + + varying vec3 vColor; + + void main() { + gl_FragColor = vec4(vColor, 1.0); + } + + `, + ); + + const triangle = new Mesh(geometry, shader); + + triangle.position.set(400, 300); + triangle.scale.set(2); + + app.stage.addChild(triangle); + + app.ticker.add(() => + { + triangle.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/triangleTextured.js b/src/examples/v8.0.0-rc.1/meshAndShaders/triangleTextured.js new file mode 100644 index 000000000..8a5071fc5 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/triangleTextured.js @@ -0,0 +1,76 @@ +import { Application, Assets, Geometry, Shader, Texture, Mesh } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -50, 100, -50, 0, 100], + aColor: [1, 0, 0, 0, 1, 0, 0, 0, 1], + aUvs: [0, 0, 1, 0, 1, 1], + }, + }); + + const vertexSrc = ` + + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec3 aColor; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + varying vec3 vColor; + + void main() { + + vUvs = aUvs; + vColor = aColor; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`; + + const fragmentSrc = ` + + precision mediump float; + + varying vec3 vColor; + varying vec2 vUvs; + + uniform sampler2D uSampler2; + + void main() { + + gl_FragColor = texture2D(uSampler2, vUvs) * vec4(vColor, 1.0); + }`; + + const uniforms = { uSampler2: texture }; + + const shader = Shader.from(vertexSrc, fragmentSrc, uniforms); + + const triangle = new Mesh(geometry, shader); + + triangle.position.set(400, 300); + triangle.scale.set(2); + + app.stage.addChild(triangle); + + app.ticker.add(() => + { + triangle.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/meshAndShaders/uniforms.js b/src/examples/v8.0.0-rc.1/meshAndShaders/uniforms.js new file mode 100644 index 000000000..e15052d83 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/meshAndShaders/uniforms.js @@ -0,0 +1,78 @@ +import { Application, Assets, Geometry, Shader, Mesh } from 'js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/bg_scene_rotate.jpg'); + + const geometry = new Geometry({ + attributes: { + aVertexPosition: [-100, -100, 100, -100, 100, 100, -100, 100], + aUvs: [0, 0, 1, 0, 1, 1, 0, 1], + indexBuffer: [0, 1, 2, 0, 2, 3], + }, + }); + + const vertexSrc = ` + + precision mediump float; + + attribute vec2 aVertexPosition; + attribute vec2 aUvs; + + uniform mat3 translationMatrix; + uniform mat3 projectionMatrix; + + varying vec2 vUvs; + + void main() { + + vUvs = aUvs; + gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0); + + }`; + + const fragmentSrc = ` + + precision mediump float; + + varying vec2 vUvs; + + uniform sampler2D uSampler2; + uniform float time; + + void main() { + + gl_FragColor = texture2D(uSampler2, vUvs + sin( (time + (vUvs.x) * 14.) ) * 0.1 ); + }`; + + const uniforms = { + uSampler2: texture, + time: 0, + }; + + const shader = Shader.from(vertexSrc, fragmentSrc, uniforms); + + const quad = new Mesh(geometry, shader); + + quad.position.set(400, 300); + quad.scale.set(2); + + app.stage.addChild(quad); + + // Start the animation.. + app.ticker.add((delta) => + { + quad.rotation += 0.01; + quad.shader.uniforms.time += 0.1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/offscreenCanvas/basic.js b/src/examples/v8.0.0-rc.1/offscreenCanvas/basic.js new file mode 100644 index 000000000..455229a0b --- /dev/null +++ b/src/examples/v8.0.0-rc.1/offscreenCanvas/basic.js @@ -0,0 +1,52 @@ +import { Application, Assets, Container, Sprite } from 'pixi.js'; + +// This example is the based on basic/container, but using OffscreenCanvas. + +const canvas = document.createElement('canvas'); +const view = canvas.transferControlToOffscreen(); + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ view, background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const container = new Container(); + + app.stage.addChild(container); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create a 5x5 grid of bunnies + for (let i = 0; i < 25; i++) + { + const bunny = new Sprite(texture); + + bunny.anchor.set(0.5); + bunny.x = (i % 5) * 40; + bunny.y = Math.floor(i / 5) * 40; + container.addChild(bunny); + } + + // Move container to the center + container.x = app.screen.width / 2; + container.y = app.screen.height / 2; + + // Center bunny sprite in local container coordinates + container.pivot.x = container.width / 2; + container.pivot.y = container.height / 2; + + // Listen for animate update + app.ticker.add((time) => + { + // Rotate the container! + // * use delta to create frame-independent transform * + container.rotation -= 0.01 * time.deltaTime; + }); +})(); diff --git a/src/examples/v8.0.0-rc/offscreenCanvas/webWorker.js b/src/examples/v8.0.0-rc.1/offscreenCanvas/webWorker.js similarity index 100% rename from src/examples/v8.0.0-rc/offscreenCanvas/webWorker.js rename to src/examples/v8.0.0-rc.1/offscreenCanvas/webWorker.js diff --git a/src/examples/v8.0.0-rc.1/sprite/animatedSpriteAnimationSpeed.js b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteAnimationSpeed.js new file mode 100644 index 000000000..4dda72f68 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteAnimationSpeed.js @@ -0,0 +1,55 @@ +import { Application, Assets, AnimatedSprite, Texture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ autoStart: false, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the animation sprite sheet + const spritesheet = await Assets.load('https://pixijs.com/assets/spritesheet/0123456789.json'); + + // Create an array to store the textures + const textures = []; + let i; + + for (i = 0; i < 10; i++) + { + const framekey = `0123456789 ${i}.ase`; + const texture = Texture.from(framekey); + const time = spritesheet.data.frames[framekey].duration; + + textures.push({ texture, time }); + } + + const scaling = 4; + + // Create a slow AnimatedSprite + const slow = new AnimatedSprite(textures); + + slow.anchor.set(0.5); + slow.scale.set(scaling); + slow.animationSpeed = 0.5; + slow.x = (app.screen.width - slow.width) / 2; + slow.y = app.screen.height / 2; + slow.play(); + app.stage.addChild(slow); + + // Create a fast AnimatedSprite + const fast = new AnimatedSprite(textures); + + fast.anchor.set(0.5); + fast.scale.set(scaling); + fast.x = (app.screen.width + fast.width) / 2; + fast.y = app.screen.height / 2; + fast.play(); + app.stage.addChild(fast); + + // Start animating + app.start(); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/animatedSpriteExplosion.js b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteExplosion.js new file mode 100644 index 000000000..6c88226a2 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteExplosion.js @@ -0,0 +1,45 @@ +import { Application, Assets, AnimatedSprite, Texture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ autoStart: false, resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the animation sprite sheet + const texture = await Assets.load('https://pixijs.com/assets/spritesheet/mc.json'); + + // Create an array to store the textures + const explosionTextures = []; + let i; + + for (i = 0; i < 26; i++) + { + const texture = Texture.from(`Explosion_Sequence_A ${i + 1}.png`); + + explosionTextures.push(texture); + } + + // Create and randomly place the animated explosion sprites on the stage + for (i = 0; i < 50; i++) + { + // Create an explosion AnimatedSprite + const explosion = new AnimatedSprite(explosionTextures); + + explosion.x = Math.random() * app.screen.width; + explosion.y = Math.random() * app.screen.height; + explosion.anchor.set(0.5); + explosion.rotation = Math.random() * Math.PI; + explosion.scale.set(0.75 + Math.random() * 0.5); + explosion.gotoAndPlay((Math.random() * 26) | 0); + app.stage.addChild(explosion); + } + + // Start animating + app.start(); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/animatedSpriteJet.js b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteJet.js new file mode 100644 index 000000000..4a0fc8b59 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/animatedSpriteJet.js @@ -0,0 +1,48 @@ +import { Application, Assets, AnimatedSprite, Texture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the animation sprite sheet + await Assets.load('https://pixijs.com/assets/spritesheet/fighter.json'); + + // Create an array of textures from the sprite sheet + const frames = []; + + for (let i = 0; i < 30; i++) + { + const val = i < 10 ? `0${i}` : i; + + // Magically works since the spritesheet was loaded with the pixi loader + frames.push(Texture.from(`rollSequence00${val}.png`)); + } + + // Create an AnimatedSprite (brings back memories from the days of Flash, right ?) + const anim = new AnimatedSprite(frames); + + /* + * An AnimatedSprite inherits all the properties of a PIXI sprite + * so you can change its position, its anchor, mask it, etc + */ + anim.x = app.screen.width / 2; + anim.y = app.screen.height / 2; + anim.anchor.set(0.5); + anim.animationSpeed = 0.5; + anim.play(); + + app.stage.addChild(anim); + + // Animate the rotation + app.ticker.add(() => + { + anim.rotation += 0.01; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/basic.js b/src/examples/v8.0.0-rc.1/sprite/basic.js new file mode 100644 index 000000000..5955df274 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/basic.js @@ -0,0 +1,37 @@ +import { Application, Assets, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + // Create a bunny Sprite + const bunny = new Sprite(texture); + + // Center the sprite's anchor point + bunny.anchor.set(0.5); + + // Move the sprite to the center of the screen + bunny.x = app.screen.width / 2; + bunny.y = app.screen.height / 2; + + app.stage.addChild(bunny); + + // Listen for animate update + app.ticker.add((time) => + { + // Just for fun, let's rotate mr rabbit a little. + // * Delta is 1 if running at 100% performance * + // * Creates frame-independent transformation * + bunny.rotation += 0.1 * time.deltaTime; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/textureSwap.js b/src/examples/v8.0.0-rc.1/sprite/textureSwap.js new file mode 100644 index 000000000..0898a6e98 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/textureSwap.js @@ -0,0 +1,47 @@ +import { Application, Assets, Texture, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the textures + const alien1texture = await Assets.load('https://pixijs.com/assets/flowerTop.png'); + const alien2texture = await Assets.load('https://pixijs.com/assets/eggHead.png'); + + let isAlien1 = true; + + // Create a new alien Sprite using the 1st texture and add it to the stage + const character = new Sprite(alien1texture); + + // Center the sprites anchor point + character.anchor.set(0.5); + + // Move the sprite to the center of the screen + character.x = app.screen.width / 2; + character.y = app.screen.height / 2; + + app.stage.addChild(character); + + // Make the sprite interactive + character.eventMode = 'static'; + character.cursor = 'pointer'; + + character.on('pointertap', () => + { + isAlien1 = !isAlien1; + // Dynamically swap the texture + character.texture = isAlien1 ? alien1texture : alien2texture; + }); + + app.ticker.add(() => + { + character.rotation += 0.02; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/tilingSprite.js b/src/examples/v8.0.0-rc.1/sprite/tilingSprite.js new file mode 100644 index 000000000..576327c1b --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/tilingSprite.js @@ -0,0 +1,42 @@ +import { Application, Assets, TilingSprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the tile texture + const texture = await Assets.load('https://pixijs.com/assets/p2.jpeg'); + + /* Create a tiling sprite and add it to the stage... + * requires a texture, a width and a height + * in WebGL the image size should preferably be a power of two + */ + const tilingSprite = new TilingSprite({ + texture, + width: app.screen.width, + height: app.screen.height, + }); + + app.stage.addChild(tilingSprite); + + let count = 0; + + // Animate the tiling sprite + app.ticker.add(() => + { + count += 0.005; + + tilingSprite.tileScale.x = 2 + Math.sin(count); + tilingSprite.tileScale.y = 2 + Math.cos(count); + + tilingSprite.tilePosition.x += 1; + tilingSprite.tilePosition.y += 1; + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/sprite/video.js b/src/examples/v8.0.0-rc.1/sprite/video.js new file mode 100644 index 000000000..e607efe76 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/sprite/video.js @@ -0,0 +1,60 @@ +import { Application, Assets, Graphics, Sprite } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Create play button that can be used to trigger the video + const button = new Graphics() + .roundRect(0, 0, 100, 100, 10) + .fill(0xffffff, 0.5) + .beginPath() + .moveTo(36, 30) + .lineTo(36, 70) + .lineTo(70, 50) + .closePath() + .fill(0xffffff); + + // Position the button + button.x = (app.screen.width - button.width) / 2; + button.y = (app.screen.height - button.height) / 2; + + // Enable interactivity on the button + button.eventMode = 'static'; + button.cursor = 'pointer'; + + // Add to the stage + app.stage.addChild(button); + + // Load the video texture + const texture = await Assets.load('https://pixijs.com/assets/video.mp4'); + + // Listen for a click/tap event to start playing the video + // this is useful for some mobile platforms. For example: + // ios9 and under cannot render videos in PIXI without a + // polyfill - https://github.com/bfred-it/iphone-inline-video + // ios10 and above require a click/tap event to render videos + // that contain audio in PIXI. Videos with no audio track do + // not have this requirement + button.on('pointertap', () => + { + // Don't need the button anymore + button.destroy(); + + // Create a new Sprite using the video texture (yes it's that easy) + const videoSprite = new Sprite(texture); + + // Stretch to fill the whole screen + videoSprite.width = app.screen.width; + videoSprite.height = app.screen.height; + + app.stage.addChild(videoSprite); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/text/bitmapText.js b/src/examples/v8.0.0-rc.1/text/bitmapText.js new file mode 100644 index 000000000..2da0aa753 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/text/bitmapText.js @@ -0,0 +1,31 @@ +import { Application, Assets, Text } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load bitmap font + await Assets.load('https://pixijs.com/assets/bitmap-font/desyrel.xml'); + + const bitmapFontText = new Text({ + text: 'bitmap fonts are supported!\nWoo yay!', + style: { + fontFamily: 'Desyrel', + fontSize: 55, + align: 'left', + }, + renderMode: 'bitmap', + }); + + bitmapFontText.x = 50; + bitmapFontText.y = 200; + + app.stage.addChild(bitmapFontText); +})(); diff --git a/src/examples/v8.0.0-rc.1/text/fromFont.js b/src/examples/v8.0.0-rc.1/text/fromFont.js new file mode 100644 index 000000000..0e83b6bf8 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/text/fromFont.js @@ -0,0 +1,38 @@ +import { Application, Assets, Text } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Add font files to the bundle + Assets.addBundle('fonts', [ + { alias: 'ChaChicle', src: 'https://pixijs.com/assets/webfont-loader/ChaChicle.ttf' }, + { alias: 'Lineal', src: 'https://pixijs.com/assets/webfont-loader/Lineal.otf' }, + { alias: 'Dotrice Regular', src: 'https://pixijs.com/assets/webfont-loader/Dotrice-Regular.woff' }, + { alias: 'Crosterian', src: 'https://pixijs.com/assets/webfont-loader/Crosterian.woff2' }, + ]); + + // Load the font bundle + await Assets.loadBundle('fonts'); + + const text1 = new Text({ text: 'ChaChicle.ttf', style: { fontFamily: 'ChaChicle', fontSize: 50 } }); + const text2 = new Text({ text: 'Lineal.otf', style: { fontFamily: 'Lineal', fontSize: 50 } }); + const text3 = new Text({ text: 'Dotrice Regular.woff', style: { fontFamily: 'Dotrice Regular', fontSize: 50 } }); + const text4 = new Text({ text: 'Crosterian.woff2', style: { fontFamily: 'Crosterian', fontSize: 50 } }); + + text2.y = 150; + text3.y = 300; + text4.y = 450; + + app.stage.addChild(text1); + app.stage.addChild(text2); + app.stage.addChild(text3); + app.stage.addChild(text4); +})(); diff --git a/src/examples/v8.0.0-rc.1/text/pixiText.js b/src/examples/v8.0.0-rc.1/text/pixiText.js new file mode 100644 index 000000000..7d96c9df0 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/text/pixiText.js @@ -0,0 +1,86 @@ +import { Application, Text, TextStyle, Color, FillGradient } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const basicText = new Text({ text: 'Basic text in pixi' }); + + basicText.x = 50; + basicText.y = 100; + + app.stage.addChild(basicText); + + // Create gradient fill + const fill = new FillGradient(0, 0, 0, 36 * 1.7 * 7); + + const colors = [0xffffff, 0x00ff99].map((color) => Color.shared.setValue(color).toNumber()); + + colors.forEach((number, index) => + { + const ratio = index / colors.length; + + fill.addColorStop(ratio, number); + }); + + const style = new TextStyle({ + fontFamily: 'Arial', + fontSize: 36, + fontStyle: 'italic', + fontWeight: 'bold', + fill: { fill }, + stroke: { color: '#4a1850', width: 5, join: 'round' }, + dropShadow: { + color: '#000000', + blur: 4, + angle: Math.PI / 6, + distance: 6, + }, + wordWrap: true, + wordWrapWidth: 440, + }); + + const richText = new Text({ + text: 'Rich text with a lot of options and across multiple lines', + style, + }); + + richText.x = 50; + richText.y = 220; + + app.stage.addChild(richText); + + const skewStyle = new TextStyle({ + fontFamily: 'Arial', + dropShadow: { + alpha: 0.8, + angle: 2.1, + blur: 4, + color: '0x111111', + distance: 10, + }, + fill: '#ffffff', + stroke: { color: '#004620', width: 12, join: 'round' }, + fontSize: 60, + fontWeight: 'lighter', + }); + + const skewText = new Text({ + text: 'SKEW IS COOL', + style: skewStyle, + }); + + skewText.skew.set(0.65, -0.3); + skewText.anchor.set(0.5, 0.5); + skewText.x = 300; + skewText.y = 480; + + app.stage.addChild(skewText); +})(); diff --git a/src/examples/v8.0.0-rc.1/text/webFont.js b/src/examples/v8.0.0-rc.1/text/webFont.js new file mode 100644 index 000000000..1f7ada7f1 --- /dev/null +++ b/src/examples/v8.0.0-rc.1/text/webFont.js @@ -0,0 +1,47 @@ +import { Application, Text } from 'pixi.js'; + +// Load them google fonts before starting... +window.WebFontConfig = { + google: { + families: ['Snippet'], + }, + active() + { + init(); + }, +}; + +/* eslint-disable */ +// include the web-font loader script +(function () { + const wf = document.createElement('script'); + wf.src = `${ + document.location.protocol === 'https:' ? 'https' : 'http' + }://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js`; + wf.type = 'text/javascript'; + wf.async = 'true'; + const s = document.getElementsByTagName('script')[0]; + s.parentNode.insertBefore(wf, s); +})(); +/* eslint-enabled */ + +async function init() { + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // create some white text using the Snippet webfont + const textSample = new Text('PixiJS text using the\ncustom "Snippet" Webfont', { + fontFamily: 'Snippet', + fontSize: 50, + fill: 'white', + align: 'left', + }); + textSample.position.set(50, 200); + app.stage.addChild(textSample); +} diff --git a/src/examples/v8.0.0-rc.1/textures/renderTextureAdvanced.js b/src/examples/v8.0.0-rc.1/textures/renderTextureAdvanced.js new file mode 100644 index 000000000..ae1efcb1a --- /dev/null +++ b/src/examples/v8.0.0-rc.1/textures/renderTextureAdvanced.js @@ -0,0 +1,108 @@ +import { Application, Assets, Container, Sprite, RenderTexture } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const stageSize = { + width: app.screen.width, + height: app.screen.height, + }; + + // Create two render textures... these dynamic textures will be used to draw the scene into itself + let renderTexture = RenderTexture.create(stageSize); + let renderTexture2 = RenderTexture.create(stageSize); + const currentTexture = renderTexture; + + // Create a new sprite that uses the render texture we created above + const outputSprite = new Sprite(currentTexture); + + // Align the sprite + outputSprite.x = 400; + outputSprite.y = 300; + outputSprite.anchor.set(0.5); + + // Add to stage + app.stage.addChild(outputSprite); + + const stuffContainer = new Container(); + + stuffContainer.x = 400; + stuffContainer.y = 300; + + app.stage.addChild(stuffContainer); + + // Create an array of image ids.. + const fruits = [ + 'https://pixijs.com/assets/rt_object_01.png', + 'https://pixijs.com/assets/rt_object_02.png', + 'https://pixijs.com/assets/rt_object_03.png', + 'https://pixijs.com/assets/rt_object_04.png', + 'https://pixijs.com/assets/rt_object_05.png', + 'https://pixijs.com/assets/rt_object_06.png', + 'https://pixijs.com/assets/rt_object_07.png', + 'https://pixijs.com/assets/rt_object_08.png', + ]; + + // Load the textures + await Assets.load(fruits); + + // Create an array of items + const items = []; + + // Now create some items and randomly position them in the stuff container + for (let i = 0; i < 20; i++) + { + const item = Sprite.from(fruits[i % fruits.length]); + + item.x = Math.random() * 400 - 200; + item.y = Math.random() * 400 - 200; + item.anchor.set(0.5); + stuffContainer.addChild(item); + items.push(item); + } + + // Used for spinning! + let count = 0; + + app.ticker.add(() => + { + for (let i = 0; i < items.length; i++) + { + // rotate each item + const item = items[i]; + + item.rotation += 0.1; + } + + count += 0.01; + + // Swap the buffers ... + const temp = renderTexture; + + renderTexture = renderTexture2; + renderTexture2 = temp; + + // Set the new texture + outputSprite.texture = renderTexture; + + // Twist this up! + stuffContainer.rotation -= 0.01; + outputSprite.scale.set(1 + Math.sin(count) * 0.2); + + // Render the stage to the texture + // * The 'true' clears the texture before the content is rendered * + app.renderer.render({ + container: app.stage, + target: renderTexture2, + clear: false, + }); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/textures/renderTextureBasic.js b/src/examples/v8.0.0-rc.1/textures/renderTextureBasic.js new file mode 100644 index 000000000..bdbb3a46e --- /dev/null +++ b/src/examples/v8.0.0-rc.1/textures/renderTextureBasic.js @@ -0,0 +1,57 @@ +import { Application, Assets, Container, Sprite, RenderTexture, SCALE_MODES } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ background: '#1099bb', resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + const container = new Container(); + + app.stage.addChild(container); + + // Load the bunny texture + const texture = await Assets.load('https://pixijs.com/assets/bunny.png'); + + for (let i = 0; i < 25; i++) + { + const bunny = new Sprite(texture); + + bunny.x = (i % 5) * 30; + bunny.y = Math.floor(i / 5) * 30; + bunny.rotation = Math.random() * (Math.PI * 2); + container.addChild(bunny); + } + + const rt = RenderTexture.create({ + width: 300, + height: 300, + scaleMode: SCALE_MODES.LINEAR, + resolution: 1, + }); + + const sprite = new Sprite(rt); + + sprite.x = 450; + sprite.y = 60; + app.stage.addChild(sprite); + + /* + * All the bunnies are added to the container with the addChild method + * when you do this, all the bunnies become children of the container, and when a container moves, + * so do all its children. + * This gives you a lot of flexibility and makes it easier to position elements on the screen + */ + container.x = 100; + container.y = 60; + + app.ticker.add(() => + { + app.renderer.render(container, { renderTexture: rt }); + }); +})(); diff --git a/src/examples/v8.0.0-rc.1/textures/textureRotate.js b/src/examples/v8.0.0-rc.1/textures/textureRotate.js new file mode 100644 index 000000000..22c883dff --- /dev/null +++ b/src/examples/v8.0.0-rc.1/textures/textureRotate.js @@ -0,0 +1,85 @@ +import { Application, Assets, Texture, Rectangle, Sprite, Text, groupD8 } from 'pixi.js'; + +(async () => +{ + // Create a new application + const app = new Application(); + + // Initialize the application + await app.init({ resizeTo: window }); + + // Append the application canvas to the document body + document.body.appendChild(app.canvas); + + // Load the texture + const texture = await Assets.load('https://pixijs.com/assets/flowerTop.png'); + + // Create rotated textures + const textures = [texture]; + const D8 = groupD8; + + for (let rotate = 1; rotate < 16; rotate++) + { + const h = D8.isVertical(rotate) ? texture.frame.width : texture.frame.height; + const w = D8.isVertical(rotate) ? texture.frame.height : texture.frame.width; + + const { frame } = texture; + const crop = new Rectangle(texture.frame.x, texture.frame.y, w, h); + const trim = crop; + let rotatedTexture; + + if (rotate % 2 === 0) + { + rotatedTexture = new Texture({ + source: texture.baseTexture, + frame, + orig: crop, + trim, + rotate, + }); + } + else + { + rotatedTexture = new Texture({ + source: texture.baseTexture, + frame, + orig: crop, + trim, + rotate, + }); + } + textures.push(rotatedTexture); + } + + const offsetX = (app.screen.width / 16) | 0; + const offsetY = (app.screen.height / 8) | 0; + const gridW = (app.screen.width / 4) | 0; + const gridH = (app.screen.height / 5) | 0; + + // Normal rotations and mirrors + for (let i = 0; i < 16; i++) + { + // Create a new Sprite using rotated texture + const dude = new Sprite(textures[i < 8 ? i * 2 : (i - 8) * 2 + 1]); + + dude.scale.x = 0.5; + dude.scale.y = 0.5; + // Show it in grid + dude.x = offsetX + gridW * (i % 4); + dude.y = offsetY + gridH * ((i / 4) | 0); + app.stage.addChild(dude); + const text = new Text({ + text: `rotate = ${dude.texture.rotate}`, + style: { + fontFamily: 'Courier New', + fontSize: '12px', + fill: 'white', + align: 'left', + }, + }); + + text.x = dude.x; + text.y = dude.y - 20; + app.stage.addChild(text); + } +})(); diff --git a/src/tutorials/index.ts b/src/tutorials/index.ts index 88dd5b4fe..88ffc5038 100644 --- a/src/tutorials/index.ts +++ b/src/tutorials/index.ts @@ -1,5 +1,5 @@ import v7x from './v7.3.2/index'; -import v8x from './v8.0.0-rc/index'; +import v8x from './v8.0.0-rc.1/index'; export type TutorialStep = { header: string; @@ -17,7 +17,7 @@ export type TutorialEntry = { // TODO: Use await import to dynamically load versioned content on demand instead? const versions: Record> = { '7.3.2': v7x, - '8.0.0-rc': v8x, + '8.0.0-rc.1': v8x, }; export function getTutorialEntry(version: string, key: string) diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/index.ts b/src/tutorials/v8.0.0-rc.1/gettingStarted/index.ts similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/index.ts rename to src/tutorials/v8.0.0-rc.1/gettingStarted/index.ts diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step1-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step1-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step1-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step1-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step1-content.md b/src/tutorials/v8.0.0-rc.1/gettingStarted/step1-content.md similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step1-content.md rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step1-content.md diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step2-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step2-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step2-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step2-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step2-completed-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step2-completed-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step2-completed-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step2-completed-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step2-content.md b/src/tutorials/v8.0.0-rc.1/gettingStarted/step2-content.md similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step2-content.md rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step2-content.md diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step3-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step3-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step3-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step3-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step3-completed-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step3-completed-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step3-completed-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step3-completed-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step3-content.md b/src/tutorials/v8.0.0-rc.1/gettingStarted/step3-content.md similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step3-content.md rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step3-content.md diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step4-code.js b/src/tutorials/v8.0.0-rc.1/gettingStarted/step4-code.js similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step4-code.js rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step4-code.js diff --git a/src/tutorials/v8.0.0-rc/gettingStarted/step4-content.md b/src/tutorials/v8.0.0-rc.1/gettingStarted/step4-content.md similarity index 100% rename from src/tutorials/v8.0.0-rc/gettingStarted/step4-content.md rename to src/tutorials/v8.0.0-rc.1/gettingStarted/step4-content.md diff --git a/src/tutorials/v8.0.0-rc/index.ts b/src/tutorials/v8.0.0-rc.1/index.ts similarity index 100% rename from src/tutorials/v8.0.0-rc/index.ts rename to src/tutorials/v8.0.0-rc.1/index.ts diff --git a/src/tutorials/v8.0.0-rc/tutorialsData.json b/src/tutorials/v8.0.0-rc.1/tutorialsData.json similarity index 100% rename from src/tutorials/v8.0.0-rc/tutorialsData.json rename to src/tutorials/v8.0.0-rc.1/tutorialsData.json