关于javascript:用js玩转Scriptable超简单教程

9次阅读

共计 20497 个字符,预计需要花费 52 分钟才能阅读完成。

前言

ios 用户当更新到 iOS14 后,咱们的 iPhone 等 ios 设施反对咱们用户自定义桌面小物件(又或者称之为小组件、桌面挂件),利用这个个性,网上呈现了许许多多诸如通明时钟、微博热搜、知乎热榜、网易云热评、特斯拉、BMW、名爵、奥迪等等的 iPhone 桌面,看如下理论效果图:

那这到底是怎么实现的,咱们怎么能力制作一款本人的 iPhone 共性桌面?明天给大家分享的就是 Scriptable 的桌面玩法,对于 javascript 开发人员来说,看完这篇教程,上手小物件开发利用是信手拈来的事儿,而对于没有编程根底的同学不必放心看不懂,你所要做的就是复制粘贴,间接跳过开发教程,看文章开端快速通道即可。

Scriptable 介绍

这是一款可让您应用 JavaScript 自动化构建 iOS 的应用程序

以上是对 Scriptable 的官网解释,这对前端开发者来说无疑是一个福音,因为 Scriptable 应用 Apple 的 JavaScriptCore,它默认就反对 ECMAScript 6 对小组件进行开发构建。

如果您刚刚开始应用 JavaScript,您可能想看看 Codecademys Intro to Programming in JavaScript。无关 JavaScript 性能的疾速参考,您能够参考 W3Schools 的 JavaScript 教程。

请留神,一些指南和教程会假如您在浏览器中运行 JavaScript,因而能够拜访特定于浏览器的对象,例如文档。Scriptable 不在浏览器中运行 JavaScript,因而不存在此类对象。

更多对于 Scriptable 的解释请浏览官网文档

要害个性

先看一张图:

下面列举的是一些 Scriptable 的个性,这些个性包含:

  • 反对 ES6 语法
  • 能够应用 JavaScript 调用一些原生的 API
  • Siri 快捷方式
  • 欠缺的文档反对
  • 共享表格扩大
  • 文件系统继承
  • 编辑器的自定义
  • 代码样例
  • 以及通过 x -callback-url 和其它 APP 交互

是不是感觉反对的个性还是挺多的,这些个性曾经足够让咱们去实现很多原生级底层的交互了。

第一个小物件程序

// 判断是否是运行在桌面的组件中
if (config.runsInWidget) {
  // 创立一个显示元素列表的小部件
  // 显示元素列表的小部件。将小部件传递给 Script.setWidget() 将其显示在您的主屏幕上。// 请留神,小部件会定期刷新,小部件刷新的速率很大水平上取决于操作系统。// 另请留神,在小部件中运行脚本时存在内存限度。当应用太多内存时,小部件将解体并且无奈正确出现。const widget = new ListWidget();
  // 增加文本物件
  const text = widget.addText("Hello, World!");
  // 设置字体色彩
  text.textColor = new Color("#000000");
  // 设置字体大小
  text.font = Font.boldSystemFont(36);
  // 设置文字对齐形式
  text.centerAlignText();
  // 新建线性突变物件
  const gradient = new LinearGradient();
  // 每种色彩的地位, 每个地位应该是 0 到 1 范畴内的值,并批示突变 colors 数组中每种色彩的地位
  gradient.locations = [0, 1];
  // 突变的色彩。locations 色彩数组应蕴含与突变属性雷同数量的元素。gradient.colors = [new Color("#F5DB1A"), new Color("#F3B626")];
  // 把设置好的渐变色配置给显示元素列表的小部件背景
  widget.backgroundGradient = gradient;
  // 设置部件
  Script.setWidget(widget);
}

通过以上简略的显示 ”Hello, World!” 并设置背景色和文字款式的程序来看,有一个重要的概念须要 javascript 程序员去了解和从传统的 web 开发的概念中转换过去,如果你之前有开发过 Flutter 开发教训的话,那么对你来说,开发 Scriptable 利用应该是有共鸣的。因为对于我看来,Scriptable 同样也是万物皆组件(widget)的概念,撑持这一点的一个重要思维就是面向对象。

万物皆组件

何为万物皆组件?无论是容器(div)还是款式(color、style)还是元素(font)等等全是 Object,比方你要显示一行文字 ”Hello, World!”,那么你首先必须要有一个容器(div)去装载这行文字(fonts),你还要去给文字设置款式(styles),那样式也不是说凭空生成,但凡对象,都要 new 进去。对照以上 ”Hello, World!” 的例子再深刻了解这个概念。

以上概念对 Scriptable 利用开发有极其重要的踊跃作用,尤其是对于高级前端开发者或没有原生 app 开发教训的开发者来说,他们很难脱离传统 web 这种 mvvc 或者 mvc 的开发模式去思考面向对象的开发模式。

高频罕用的组件

ListWidge

显示元素列表的小部件,最罕用的容器组件。个别组件利用的根元素都用 ListWidget 包裹,也只有用这个组件能力传递给 Script.setWidget() 将其显示在您的主屏幕上。

请留神,小部件会定期刷新,并且小部件刷新的速率很大水平上取决于操作系统。留神:利用这一点能够做很多须要基于定时刷新的利用,比方:节日纪念日,须要计算以后工夫的利用。

另请留神,在小部件中运行脚本时存在内存限度。当应用太多内存时,小部件将解体并且无奈正确出现。

-addStack

addStack(): WidgetStack

增加堆栈。

ListWidget.addStack()返回值是 WidgetStack(堆栈元素),将堆栈元素增加到 ListWidget 中是程度布局的,能够利用这个api 实现相似于 flex 布局

-addSpacer

addSpacer(length: number): WidgetSpacer

向小部件增加距离。这可用于在小部件中垂直偏移内容。相似于 web 开发中 css 的margin

-setPadding

setPadding(top: number, leading: number, bottom: number, trailing: number)

设置小部件每一侧的填充。相似 web 中 css 的padding

-addText

addText(text: string): WidgetText

将文本元素增加到小部件。应用返回元素的属性来设置文本款式。类比 web 开发中的向 div 中插入文本节点。

backgroundColor

backgroundColor: Color

设置容器的背景色彩,值必须是 Color 类型(new Color('#fff', 1)),Color 构造函数的第一个参数为色值,第二个参数为透明度,相似 web 开发中的 rgba(255,255,255,1)

backgroundImage

backgroundImage: Image

设置容器的背景图片。相似 web 中 css 的backgroud-image

Font

示意字体和文本大小。

new Font(name: string, size: number)

该字体可用于设置文本款式,例如在小部件中。

– regularSystemFont

创立惯例零碎字体。

static regularSystemFont(size: number): Font

-lightSystemFont

创立白天模式零碎字体。

static lightSystemFont(size: number): Font

-thinSystemFont

创立细零碎字体。

static thinSystemFont(size: number): Font

Keychain

钥匙串是凭据、密钥等的平安存储。应用该 set() 办法将值增加到钥匙串。而后,您能够稍后应用该 get() 办法检索该值。

-contains

查看钥匙串是否蕴含钥匙。

static contains(key: string): bool

查看钥匙串是否蕴含指定的钥匙。

-set

将指定键的值增加到钥匙串。

static set(key: string, value: string)

将值增加到钥匙串,将其调配给指定的键。如果密钥已存在于钥匙串中,则该值将被笼罩。

值平安地存储在加密数据库中。

-get

从钥匙串中读取一个值。

static get(key: string): string

读取指定键的值。如果密钥不存在,该办法将引发谬误。应用该 contains 办法查看钥匙串中是否存在钥匙。

Alert

显示模态弹窗。相似 web ui 中的 Modal 组件

应用它来配置以模态或表单模式出现的弹窗。配置弹窗后,调用 presentAlert() 或 presentSheet() 以出现弹窗。这两种示意办法将返回一个值,该值携带实现时抉择的操作的索引。比方你弹窗增加了两个操作按钮,先增加一个是 确定 ,另一个是 勾销 按钮,增加操作跟 js 中的数组统一,先增加的按钮索引就是 0,当用户点击 确认 按钮的时候,alert.presentAlert()返回的值就是 ’ 确认 ’ 在配置数组中的索引值,即为 0。

集体认为这个组件也是十分高频的组件,因为在高级桌面组件或者简单的组件,尤其是一些须要用户登录账号信息的桌面组件来说,须要弹窗让用户输出账号密码等交互行为,又或者让用户输出日期、名称等须要长久化存储的场景,Alert 组件是不二之选。

-message

title: string

弹窗中显示的题目。通常是一个短字符串。

-addAction

向弹窗中增加操作按钮。要查看是否抉择了某个操作,您应该应用在 presentAlert() 和 presentSheet() 返回的 Promise 时提供的第一个参数。

// 创立一个弹窗组件
let alert = new Alert();
// 设置弹窗中显示的 content
alert.message = '弹窗中显示的内容,这里能够展现对操作的解释等文案信息...';
// 向弹窗中退出一个按钮 - 确定,索引为 0
alert.addAction('确定');
// 向弹窗中退出一个按钮 - 勾销,所以为 1
alert.addAction('勾销');
// 获取弹窗按钮被触发后拿到用户点击的具体某个按钮索引,如果点击确定,response === 0 否则 response === 1
let response = await alert.presentAlert();
-addCancelAction

addCancelAction(title: string)

向弹窗中增加勾销操作。抉择勾销操作时,kidealert()或 vistentheet()提供的索引将始终为 -1。请留神,在 iPad 上运行并应用 presentSheet() 进行演示时,该操作不会显示在操作列表中。通过在工作表外点击可勾销操作。

弹窗只能蕴含一个勾销操作。尝试增加更多勾销操作将删除之前增加的任何勾销操作。

-presentAlert

显示模态弹出窗,相似 elementuimodalvisible 设置为true,此时弹窗显示。

-presentSheet

将弹窗以相似 bottomSheet 交互方式弹出。

Image

治理图像数据。

图像对象蕴含图像数据。Scriptable 中解决图像的 API(通过将图像作为输出或返回图像)将应用此 Image 类型。

-size

size: Size

图像的大小(以像素为单位)。只读

-fromFile

从指定的文件门路加载图像。如果无奈读取图像,该函数将返回 null。相似 web 开发中读取本地(ios 中还有 iCloud)图片文件

-fromData

static fromData(data: Data): Image

从原始数据加载图像。如果无奈读取图像,该函数将返回 null。

Data 能够是字符串、文件和图像的原始数据示意。例如,Image 中用的比拟多的就是从 base64 字符串中读取图片,伪代码示例如下:

let imageDataString = 'base64:xxxxx'
let imageData = Data.fromBase64String(imageDataString)
// Convert to image and crop before returning.
let imageFromData = Image.fromData(imageData)
// return Image(imageFromData)
return imageFromData

更多对于 Data 的其余 api 请参考文档

Photos

提供对您的照片库的拜访。

为了从您的照片库中读取,您必须授予应用程序拜访您的照片库的权限。首次应用 API 时,利用会提醒拜访,但如果您拒绝请求,所有 API 调用都会失败。在这种状况下,您必须从零碎设置中启用对照片库的拜访。

这个 api 用的也是绝对高频的一个,因为大部分场景下,你的 widget 都须要用到图片或者背景,而应用图片的大部分场景(特地是背景图)都须要拜访你的设施图库,也就是你的相册,当然应用相册性能必须在用户受权的前提下。

-fromLibrary

static fromLibrary(): Promise<Image>

显示用于抉择图像的照片库,应用它从照片库中筛选图像。

应用它:

const img = await Photos.fromLibrary();
// 拿到 Image 对象后,能够对它做缓存、展现、传输等等用处
-latestPhoto

获取最新照片。

static latestPhoto(): Promise<Image>

从您的照片库中读取最新照片。如果没有可用的照片,则承诺将被回绝。

-latestScreenshot

获取最新截图。

static latestScreenshot(): Promise<Image>

从您的照片库中读取最新的屏幕截图。如果没有可用的屏幕截图,则 Promise 将被回绝。

Pasteboard

复制并粘贴字符串或图像。

从粘贴板复制和粘贴字符串和图像。

-copy

将字符串复制到粘贴板。

static copy(string: string)

-paste

从粘贴板粘贴字符串。

static paste(): string

-copyImage

将图像复制到粘贴板。

static copyImage(image: Image)

LinearGradient

线性突变。

要在小部件中应用的线性突变。

-colors

突变的色彩。

locations色彩数组应蕴含与突变属性雷同数量的元素。

colors: [Color]

相似 css 中 linear-gradient 属性的第二、三个从参数,示意突变的色彩范畴

.horizontal-gradient {background: linear-gradient(to right, blue, pink);
}
-locations

每种色彩的地位。

每个地位应该是 0 到 1 范畴内的值,并批示突变 colors 数组中每种色彩的地位。

colors地位数组应蕴含与突变属性雷同数量的元素。

locations: [number]

const bg = new LinearGradient()
bg.locations = [0, 1]
bg.colors = [new Color('#f35942', 1),
  new Color('#e92d1d', 1)
]
w.backgroundGradient = bg

FileManager

此 api 实用于做缓存数据用,比拟罕用的 api 之一,应用频次较高

-local

创立一个本地 FileManager。

static local(): FileManager

创立一个文件管理器,用于操作本地存储的文件。

const files = FileManager.local();
-iCloud

创立一个 iCloud 文件管理器。

static iCloud(): FileManager

创立一个文件管理器,用于操作存储在 iCloud 中的文件。必须在设施上启用 iCloud 能力应用它。

-read

将文件的内容作为数据读取。

read(filePath: string): Data

读取文件门路指定的文件内容作为原始数据。要将文件作为字符串 readString(filePath) 读取,请参见并将其作为图像读取,请参见readImage(filePath).

如果文件不存在或存在于 iCloud 但尚未下载,该函数将出错。用于 fileExists(filePath) 查看文件是否存在并 downloadFileFromiCloud(filePath) 下载文件。请留神,调用 始终是平安的downloadFileFromiCloud(filePath),即便文件本地存储在设施上。

-readImage

将文件的内容作为图像读取。

readImage(filePath: string): Image

读取文件门路指定的文件内容并将其转换为图像。

// 读取本人在本地缓存的图片
const img = files.readImage(files.joinPath(files.documentsDirectory(), "avatar.jpg"))
-write

将数据写入文件。

write(filePath: string, content: Data)

-writeImage

将图像写入文件。

writeImage(filePath: string, image: Image)

将图像写入磁盘上的指定文件门路。如果该文件尚不存在,则会创立该文件。如果文件曾经存在,则文件的内容将被新内容笼罩。

-fileExists

查看文件是否存在。

fileExists(filePath: string): bool

查看文件是否存在于指定的文件门路中。在挪动或复制到指标之前查看这一点可能是一个好主见,因为这些操作将替换指标文件门路中的任何现有文件。

-documentsDirectory

文档目录的门路。

documentsDirectory(): string

用于检索文档目录的门路。您的脚本存储在此目录中。如果您启用了 iCloud,您的脚本将存储在 iCloud 的文档目录中,否则它们将存储在本地文档目录中。该目录可用于长期存储。能够应用“文件”应用程序拜访存储在此目录中的文档。存储在本地文档目录中的文件不会呈现在“文件”应用程序中。

-joinPath

连贯两个门路组件。性能同 node 中的joinPath

joinPath(lhsPath: string, rhsPath: string): string

连贯两条门路以创立一条门路。例如,用文件名连贯到目录的门路。这是创立传递给 FileManager 的读取和写入函数的新文件门路的倡议办法。

封装罕用办法

网络申请

/**
   * HTTP 申请接口
   * @param {string} url 申请的 url
   * @param {bool} json 返回数据是否为 json,默认 true
   * @param {bool} useCache 是否采纳离线缓存(申请失败后获取上一次后果),* @return {string | json | null}
*/
async httpGet(url, json = true, useCache = false) {
  let data = null
  const cacheKey = this.md5(url)
  if (useCache && Keychain.contains(cacheKey)) {let cache = Keychain.get(cacheKey)
    return json ? JSON.parse(cache) : cache
  }
  try {let req = new Request(url)
    data = await (json ? req.loadJSON() : req.loadString())
  } catch (e) {}
  // 判断数据是否为空(加载失败)if (!data && Keychain.contains(cacheKey)) {
    // 判断是否有缓存
    let cache = Keychain.get(cacheKey)
    return json ? JSON.parse(cache) : cache
  }
  // 存储缓存
  Keychain.set(cacheKey, json ? JSON.stringify(data) : data)
  return data
}

获取近程图片

/**
   * 获取近程图片内容
   * @param {string} url 图片地址
   * @param {bool} useCache 是否应用缓存(申请失败时获取本地缓存)*/
async getImageByUrl(url, useCache = true) {const cacheKey = this.md5(url)
  const cacheFile = FileManager.local().joinPath(FileManager.local().temporaryDirectory(), cacheKey)
  // 判断是否有缓存
  if (useCache && FileManager.local().fileExists(cacheFile)) {return Image.fromFile(cacheFile)
  }
  try {const req = new Request(url)
    const img = await req.loadImage()
    // 存储到缓存
    FileManager.local().writeImage(cacheFile, img)
    return img
  } catch (e) {
    // 没有缓存 + 失败状况下,返回自定义的绘制图片(红色背景)throw new Error('加载图片失败');
  }
}

带透明度的背景图

async function shadowImage(img) {let ctx = new DrawContext()
  // 把画布的尺寸设置成图片的尺寸
  ctx.size = img.size
  // 把图片绘制到画布中
  ctx.drawImageInRect(img, new Rect(0, 0, img.size['width'], img.size['height']))
  // 设置绘制的图层色彩,为半透明的彩色
  ctx.setFillColor(new Color('#000000', 0.5))
  // 绘制图层
  ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height']))

  // 导出最终图片
  return await ctx.getImage()}

获取时间差

function getDistanceSpecifiedTime(dateTime) {
  // 指定日期和工夫
  var EndTime = new Date(dateTime);
  // 以后零碎工夫
  var NowTime = new Date();
  var t = EndTime.getTime() - NowTime.getTime();
  var d = Math.floor(t / 1000 / 60 / 60 / 24);
  var h = Math.floor(t / 1000 / 60 / 60 % 24);
  var m = Math.floor(t / 1000 / 60 % 60);
  var s = Math.floor(t / 1000 % 60);
  return d;
}

所有反对的手机小物件像素大小和地位

罕用来设置伪通明背景

// Pixel sizes and positions for widgets on all supported phones.
function phoneSizes() {
  let phones = {
    // 12 and 12 Pro
    "2532": {
      small:  474,
      medium: 1014,
      large:  1062,
      left:  78,
      right: 618,
      top:    231,
      middle: 819,
      bottom: 1407
    },

    // 11 Pro Max, XS Max
    "2688": {
      small:  507,
      medium: 1080,
      large:  1137,
      left:  81,
      right: 654,
      top:    228,
      middle: 858,
      bottom: 1488
    },

    // 11, XR
    "1792": {
      small:  338,
      medium: 720,
      large:  758,
      left:  54,
      right: 436,
      top:    160,
      middle: 580,
      bottom: 1000
    },


    // 11 Pro, XS, X
    "2436": {
      small:  465,
      medium: 987,
      large:  1035,
      left:  69,
      right: 591,
      top:    213,
      middle: 783,
      bottom: 1353
    },

    // Plus phones
    "2208": {
      small:  471,
      medium: 1044,
      large:  1071,
      left:  99,
      right: 672,
      top:    114,
      middle: 696,
      bottom: 1278
    },

    // SE2 and 6/6S/7/8
    "1334": {
      small:  296,
      medium: 642,
      large:  648,
      left:  54,
      right: 400,
      top:    60,
      middle: 412,
      bottom: 764
    },


    // SE1
    "1136": {
      small:  282,
      medium: 584,
      large:  622,
      left: 30,
      right: 332,
      top:  59,
      middle: 399,
      bottom: 399
    },

    // 11 and XR in Display Zoom mode
    "1624": {
      small: 310,
      medium: 658,
      large: 690,
      left: 46,
      right: 394,
      top: 142,
      middle: 522,
      bottom: 902 
    },

    // Plus in Display Zoom mode
    "2001" : {
      small: 444,
      medium: 963,
      large: 972,
      left: 81,
      right: 600,
      top: 90,
      middle: 618,
      bottom: 1146
    }
  }
  return phones
}

获取截图中的组件剪裁图

/**
   * 获取截图中的组件剪裁图
   * 可用作通明背景
   * 返回图片 image 对象
   * 代码改自:https://gist.github.com/mzeryck/3a97ccd1e059b3afa3c6666d27a496c9
   * @param {string} title 开始解决前提醒用户截图的信息,可选(适宜用在组件自定义通明背景时提醒)*/
async getWidgetScreenShot (title = null) {
  // Generate an alert with the provided array of options.
  async function generateAlert(message,options) {let alert = new Alert()
    alert.message = message

    for (const option of options) {alert.addAction(option)
    }

    let response = await alert.presentAlert()
    return response
  }

  // Crop an image into the specified rect.
  function cropImage(img,rect) {let draw = new DrawContext()
    draw.size = new Size(rect.width, rect.height)

    draw.drawImageAtPoint(img,new Point(-rect.x, -rect.y))  
    return draw.getImage()}

  async function blurImage(img,style) {
    const blur = 150
    const js = `
var mul_table=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];var shg_table=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function stackBlurCanvasRGB(id,top_x,top_y,width,height,radius){if(isNaN(radius)||radius<1)return;radius|=0;var canvas=document.getElementById(id);var context=canvas.getContext("2d");var imageData;try{try{imageData=context.getImageData(top_x,top_y,width,height)}catch(e){try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");imageData=context.getImageData(top_x,top_y,width,height)}catch(e){alert("Cannot access local image");throw new Error("unable to access local image data:"+e);return}}}catch(e){alert("Cannot access image");throw new Error("unable to access image data:"+e);}var pixels=imageData.data;var x,y,i,p,yp,yi,yw,r_sum,g_sum,b_sum,r_out_sum,g_out_sum,b_out_sum,r_in_sum,g_in_sum,b_in_sum,pr,pg,pb,rbs;var div=radius+radius+1;var w4=width<<2;var widthMinus1=width-1;var heightMinus1=height-1;var radiusPlus1=radius+1;var sumFactor=radiusPlus1*(radiusPlus1+1)/2;var stackStart=new BlurStack();var stack=stackStart;for(i=1;i<div;i++){stack=stack.next=new BlurStack();if(i==radiusPlus1)var stackEnd=stack}stack.next=stackStart;var stackIn=null;var stackOut=null;yw=yi=0;var mul_sum=mul_table[radius];var shg_sum=shg_table[radius];for(y=0;y<height;y++){r_in_sum=g_in_sum=b_in_sum=r_sum=g_sum=b_sum=0;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=0;i<radiusPlus1;i++){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next}for(i=1;i<radiusPlus1;i++){p=yi+((widthMinus1<i?widthMinus1:i)<<2);r_sum+=(stack.r=(pr=pixels[p]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[p+1]))*rbs;b_sum+=(stack.b=(pb=pixels[p+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next}stackIn=stackStart;stackOut=stackEnd;for(x=0;x<width;x++){pixels[yi]=(r_sum*mul_sum)>>shg_sum;pixels[yi+1]=(g_sum*mul_sum)>>shg_sum;pixels[yi+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(yw+((p=x+radius+1)<widthMinus1?p:widthMinus1))<<2;r_in_sum+=(stackIn.r=pixels[p]);g_in_sum+=(stackIn.g=pixels[p+1]);b_in_sum+=(stackIn.b=pixels[p+2]);r_sum+=r_in_sum;g_sum+=g_in_sum;b_sum+=b_in_sum;stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=4}yw+=width}for(x=0;x<width;x++){g_in_sum=b_in_sum=r_in_sum=g_sum=b_sum=r_sum=0;yi=x<<2;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=0;i<radiusPlus1;i++){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next}yp=width;for(i=1;i<=radius;i++){yi=(yp+x)<<2;r_sum+=(stack.r=(pr=pixels[yi]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[yi+1]))*rbs;b_sum+=(stack.b=(pb=pixels[yi+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;if(i<heightMinus1){yp+=width}}yi=x;stackIn=stackStart;stackOut=stackEnd;for(y=0;y<height;y++){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(x+(((p=y+radiusPlus1)<heightMinus1?p:heightMinus1)*width))<<2;r_sum+=(r_in_sum+=(stackIn.r=pixels[p]));g_sum+=(g_in_sum+=(stackIn.g=pixels[p+1]));b_sum+=(b_in_sum+=(stackIn.b=pixels[p+2]));stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=width}}context.putImageData(imageData,top_x,top_y)}function BlurStack(){this.r=0;this.g=0;this.b=0;this.a=0;this.next=null}
      // https://gist.github.com/mjackson/5311256

      function rgbToHsl(r, g, b){
          r /= 255, g /= 255, b /= 255;
          var max = Math.max(r, g, b), min = Math.min(r, g, b);
          var h, s, l = (max + min) / 2;

          if(max == min){h = s = 0; // achromatic}else{
              var d = max - min;
              s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
              switch(max){case r: h = (g - b) / d + (g < b ? 6 : 0); break;
                  case g: h = (b - r) / d + 2; break;
                  case b: h = (r - g) / d + 4; break;
              }
              h /= 6;
          }

          return [h, s, l];
      }

      function hslToRgb(h, s, l){
          var r, g, b;

          if(s == 0){r = g = b = l; // achromatic}else{var hue2rgb = function hue2rgb(p, q, t){if(t < 0) t += 1;
                  if(t > 1) t -= 1;
                  if(t < 1/6) return p + (q - p) * 6 * t;
                  if(t < 1/2) return q;
                  if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
                  return p;
              }

              var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
              var p = 2 * l - q;
              r = hue2rgb(p, q, h + 1/3);
              g = hue2rgb(p, q, h);
              b = hue2rgb(p, q, h - 1/3);
          }

          return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
      }

      function lightBlur(hsl) {

        // Adjust the luminance.
        let lumCalc = 0.35 + (0.3 / hsl[2]);
        if (lumCalc < 1) {lumCalc = 1;}
        else if (lumCalc > 3.3) {lumCalc = 3.3;}
        const l = hsl[2] * lumCalc;

        // Adjust the saturation. 
        const colorful = 2 * hsl[1] * l;
        const s = hsl[1] * colorful * 1.5;

        return [hsl[0],s,l];

      }

      function darkBlur(hsl) {

        // Adjust the saturation. 
        const colorful = 2 * hsl[1] * hsl[2];
        const s = hsl[1] * (1 - hsl[2]) * 3;

        return [hsl[0],s,hsl[2]];

      }

      // Set up the canvas.
      const img = document.getElementById("blurImg");
      const canvas = document.getElementById("mainCanvas");

      const w = img.naturalWidth;
      const h = img.naturalHeight;

      canvas.style.width  = w + "px";
      canvas.style.height = h + "px";
      canvas.width = w;
      canvas.height = h;

      const context = canvas.getContext("2d");
      context.clearRect(0, 0, w, h);
      context.drawImage(img, 0, 0);

      // Get the image data from the context.
      var imageData = context.getImageData(0,0,w,h);
      var pix = imageData.data;

      var isDark = "${style}" == "dark";
      var imageFunc = isDark ? darkBlur : lightBlur;

      for (let i=0; i < pix.length; i+=4) {

        // Convert to HSL.
        let hsl = rgbToHsl(pix[i],pix[i+1],pix[i+2]);

        // Apply the image function.
        hsl = imageFunc(hsl);

        // Convert back to RGB.
        const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);

        // Put the values back into the data.
        pix[i] = rgb[0];
        pix[i+1] = rgb[1];
        pix[i+2] = rgb[2];

      }

      // Draw over the old image.
      context.putImageData(imageData,0,0);

      // Blur the image.
      stackBlurCanvasRGB("mainCanvas", 0, 0, w, h, ${blur});

      // Perform the additional processing for dark images.
      if (isDark) {

        // Draw the hard light box over it.
        context.globalCompositeOperation = "hard-light";
        context.fillStyle = "rgba(55,55,55,0.2)";
        context.fillRect(0, 0, w, h);

        // Draw the soft light box over it.
        context.globalCompositeOperation = "soft-light";
        context.fillStyle = "rgba(55,55,55,1)";
        context.fillRect(0, 0, w, h);

        // Draw the regular box over it.
        context.globalCompositeOperation = "source-over";
        context.fillStyle = "rgba(55,55,55,0.4)";
        context.fillRect(0, 0, w, h);

      // Otherwise process light images.
      } else {context.fillStyle = "rgba(255,255,255,0.4)";
        context.fillRect(0, 0, w, h);
      }

      // Return a base64 representation.
      canvas.toDataURL(); 
      `

    // Convert the images and create the HTML.
    let blurImgData = Data.fromPNG(img).toBase64String()
    let html = `
      <img id="blurImg" src="data:image/png;base64,${blurImgData}" />
      <canvas id="mainCanvas" />
      `

    // Make the web view and get its return value.
    let view = new WebView()
    await view.loadHTML(html)
    let returnValue = await view.evaluateJavaScript(js)

    // Remove the data type from the string and convert to data.
    let imageDataString = returnValue.slice(22)
    let imageData = Data.fromBase64String(imageDataString)

    // Convert to image and crop before returning.
    let imageFromData = Image.fromData(imageData)
    // return cropImage(imageFromData)
    return imageFromData
  }

创立弹窗

async function generateAlert(message, options) {let alert = new Alert();
  alert.message = message;

  for (const option of options) {alert.addAction(option);
  }

  let response = await alert.presentAlert();
  return response;
}

弹出一个告诉

/**
   * 弹出一个告诉
   * @param {string} title 告诉题目
   * @param {string} body 告诉内容
   * @param {string} url 点击后关上的 URL
*/
async notify (title, body, url, opts = {}) {let n = new Notification()
  n = Object.assign(n, opts);
  n.title = title
  n.body = body
  if (url) n.openURL = url
  return await n.schedule()}

应用教程

  1. AppStore 搜寻下载 Scriptable

  1. 关上 Scriptable,点击右上角➕,粘贴从小物件屋小程序里复制的装置小组件代码

  1. 点击右下角▶️运行按钮进行下载安装组件代码,若须要配置小物件(如: 设置背景图片等),会弹出弹窗,依据提醒下一步操作即可,若无任何反馈则示意无需配置,接下去点击左上角的 Done 按钮即可

  1. 回到 iPhone 桌面,长按,增加组件,抉择 Scriptable 利用,勾选刚刚增加的小组件代码,实现显示成果😃

快速通道

正文完
 0