这是我参与8月更文挑战的第21天,活动详情查看: 8月更文挑战
Spark AR 是 Facebook 免费创作 AR 作品的平台,使用户能够为 Facebook 和 Instagram 创建交互式增强现实体验,超过 40 万名创作者,190个国家/地区,使用 Spark AR 来创作自己的AR作品
由于该软件无需任何编码知识即可使用,因此任何人现在都可以在 AR 世界中几乎没有经验地制作下一个疯狂式传播的 Instagram AR 特效,引领世界潮流。
专门的 AR 滤镜设计师单价甚至可达到 1000 美元到 3 万美元不等。
除了 JavaScript, SparkAR Studio 还支持 TypeScript 的使用。
TypeScript 语言是 JavaScript 的超集,它遵循面向对象的编程结构,并提供额外的语言特性,如可选的静态类型和类成员的访问修饰符。
当在 Spark AR Studio 中添加一个新脚本时,你可以选择是创建一个新的 JavaScript 文件还是 TypeScript 文件。
你可以在你的效果中同时使用 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