Spark AR —— TypeScript 支持【脚本】

924 阅读6分钟

这是我参与8月更文挑战的第21天,活动详情查看: 8月更文挑战

Spark AR 是 Facebook 免费创作 AR 作品的平台,使用户能够为 Facebook 和 Instagram 创建交互式增强现实体验,超过 40 万名创作者,190个国家/地区,使用 Spark AR 来创作自己的AR作品

由于该软件无需任何编码知识即可使用,因此任何人现在都可以在 AR 世界中几乎没有经验地制作下一个疯狂式传播的 Instagram AR 特效,引领世界潮流。

专门的 AR 滤镜设计师单价甚至可达到 1000 美元到 3 万美元不等。

image.png

除了 JavaScript, SparkAR Studio 还支持 TypeScript 的使用。

TypeScript 语言是 JavaScript 的超集,它遵循面向对象的编程结构,并提供额外的语言特性,如可选的静态类型和类成员的访问修饰符。

当在 Spark AR Studio 中添加一个新脚本时,你可以选择是创建一个新的 JavaScript 文件还是 TypeScript 文件。

image.png

你可以在你的效果中同时使用 JavaScript 和 TypeScript 文件,而不需要对现有的 .js 文件做任何修改。

所有的脚本文件都应该有一个唯一的名字,不管它的扩展名是什么 —— TypeScrip t文件和 JavaScript 文件不能共享一个名字。

虽然许多 IDE 都支持 TypeScript 语言,但也有一些 IDE 提供了额外的特性来增强开发体验。例如,Visual Studio Code 提供了语法高亮显示、自动代码完成和建议代码片段等其他有用的开发特性

下面列出的是 TypeScript 提供的一些关键语言特性。

模块导入语法

从 SparkAR API 导入模块的语法在 TypeScript 中略有不同,但功能是相同的。

// How to load in modules in a TypeScript file
import Scene from 'Scene';
import Diagnostics from 'Diagnostics';

一旦导入,就可以像在 JavaScript 文件中一样使用 api。

// How to load in modules in a TypeScript file
import Diagnostics from 'Diagnostics';

// Enable async/await in TypeScript [Part 1]
(async function () {

    // Log a message to the console
    Diagnostics.log("Hello world");
    
// Enable async/await in TypeScript [Part 2]
})();

你也可以像使用 JavaScript 一样从 AR 库导入脚本包。

import Cannon from 'cannon';

// Enable async/await in TypeScript [Part 1]
(async function () {
    
    const world = new Cannon.World();
    
// Enable async/await in TypeScript [Part 2]
})();

类型注释

在 TypeScript 中,你可以选择显式指定变量、函数参数、函数返回类型或对象属性的数据类型。这与 JavaScript 等弱类型语言不同,后者根据使用推断数据类型。

类型注释的语法是:type,其中 type 是有效的 TypeScript 类型。

// Declaring variables with type annotation in TypeScript
var message: string = "Hello";
var someNumber: number = 10;
var someBoolean: boolean = true;

// Type annotations are optional, so the following declaration is also valid
// The type of 'value' is inferred as a number because of the value assigned
var value = 5;

// Type annotation for a function's parameters and return type
function addTwoNumbers(x: number, y: number): number {
    return x + y;
}

类型注释用于强制类型检查,这有助于在编译期间捕获常见的数据类型错误。

// Declaring two variables of different data types
var message: string = "Hello";
var someNumber: number = 10;

// Throws a compiler error because we are trying to assign a numerical value to an object of type 'string'
message = 10;

// Throws a compiler error because the function 'toUpperCase' does not exist for objects of type 'number'
someNumber.toUpperCase();

严格的类型也有助于提高代码的可读性和可维护性。

类型断言

可以使用类型断言通过 as 关键字将对象或值的类型设置为特定的 Spark 类型。例如:

// Locate the plane in the scene, which we know will be of type Plane
const plane = await Scene.root.findFirst('plane0') as Plane;

// Create a reference to the position property, which we know will be a PointSignal
const position = plane.transform.position as PointSignal;
                

这样做允许编译器为对象和值提供正确的类型信息和自动补全。

类型断言仅在编译时有效,不会影响代码的运行时行为。

访问修饰符

在创建类时,你可以使用 TypeScript 提供的三个访问修饰符来设置属性或方法的可访问性级别:

  • Publi c— 可以从任何地方访问公共属性或方法。如果没有指定访问修饰符,则默认分配公共修饰符。

  • Private — 私有属性或方法只能从声明它的类中访问。试图从类外部访问它会导致编译错误。

  • Protected — 受保护的属性或方法可以从声明它的同一个类和任何子类(从父类继承的类)中访问。试图从任何其他地方访问它会导致编译错误。

class Person {

    // These variables are only accessible from within the 'Person' class
    private firstName: string;
    private lastName: string;

    // This variable is accessible from within the 'Person' class, and any of its subclasses
    protected idNumber: number;
    
    constructor(firstName: string, lastName: string, idNumber: number){
        this.firstName = firstName;
        this.lastName = lastName;
        this.idNumber = idNumber;
    }

    // This function can be called from anywhere, including from outside of the class
    public getInfo(): string {        
        return `Full name: ${this.firstName} ${this.lastName}, ID: ${this.idNumber}`;
    } 
}

// Creating a new instance of 'Person'
const person = new Person("John", "Smith", 101);

// We can call this function here outside of the class because its accessibility is set to 'public'
var personInfo = person.getInfo(); // Result: Full name: John Smith, ID: 101                    
                

泛型

TypeScript 中的泛型允许你创建可重用组件,这些组件可以使用多种数据类型,而不仅仅是一种,这可以减少你需要编写和维护的代码量。

考虑以下函数,它们执行相同的逻辑,但采用不同类型的参数:

// Returns the number value that was passed as an argument
function getNumber(someNumber: number): number {
    return someNumber;
}

// Returns the string value that was passed as an argument
function getString(someString: string): string {
    return someString;
}

var firstResult = getNumber(10); // Result: 10
var secondResult = getString("Hello"); // Result: Hello

虽然可以使用 any 关键字将上面的代码转换为泛型函数,但由于该函数将接受任何数据,因此将失去类型安全性。

// Returns the value that was passed as an argument
function getValue(someValue: any): any {
    return someValue;
}

var firstResult = getValue(10); // Result: 10

// This wouldn't cause a compilation error since the function accepts any data, but could cause a runtime error if we attempt to perform an incompatible operation
var secondResult = getValue(someOtherObject);
                

通过使用泛型,我们可以创建一个函数,该函数可以接受不同数据类型的参数,同时保持类型安全。

这些泛型类型参数通过使用 或 T 来表示。

// Returns the value that was passed as an argument
// Works with multiple data types 
function getValue<T>(someValue: T): T{ 
    return someValue;
}

// When calling the function we substitute <T> with the data type of the value we are passing in
var firstResult = getValue<number>(10); // Result: 10
var secondResult = getValue<string>("Hello"); // Result: Hello

// The compiler can also infer the data type of the argument and set the type of 'T' to match, so the following is also valid
var thirdResult = getValue("A string"); // Result: A string
 

函数可以有多个泛型参数:

// This function has multiple generic parameters and returns the value of the first argument passed
function someFunction<T, U, V>(firstValue: T, secondValue: U, thirdValue: V): T{ 
    return firstValue;
}
    

类也可以是泛型的

// Create a class which 
class GenericValue<T> {
    private value: T;

    constructor(value: T){
        this.value = value;
    }

    public GetValue(): T{
        return this.value;
    }
}

// Create a new 'GenericValue' instance with a number type
var numberObject = new GenericValue<number>(10);
var firstResult = numberValue.GetValue(); // Result: 10

// Create a new 'GenericValue' instance with a string type
var stringObject = new GenericValue<string>("Hello");
var secondResult = stringValue.GetValue(); // Result: Hello
                

TypeScript 提供了许多其他语言特性,你可以点击本链接