开篇寄语
前几天,伯衡君推荐用Scriptable撰写并运行Javascript脚本,部署在iPhone和iPad桌面上,及时获取各种资讯以提高生活品质,具体可以参看下面的前情提要。本篇文章,则是汇总一些伯衡君在使用过程中觉得十分惊艳的脚本,本篇会陆续更新的,敬请关注。
前情提要
收录列表
百度热搜 | 北京一周尾号限行 | 豆瓣电影一周口碑榜 |
订阅或日期进度条 | 汇率展示 | 微博热搜Top10 |
知乎热榜Top10 | Nasa美图 | 随机菜谱 |
随机电影台词 | 随机古诗 | 随机英文名句 |
温馨提示
文中会涉及到的透明脚本效果,请参看下方两张图,手机壁纸尽量挑一些颜色不太复杂的,这样效果会非常好,有那种浑然一体的效果:
内容详情
- 百度热搜
- 取了前10新闻,以在最小的小组件显示,可根据需求自行修改,百度热搜出现的数据有20个
const { transparent } = importModule('no-background') const w = new ListWidget() w.backgroundImage = await transparent(Script.name()) const req = new Request("https://www.baidu.com/"); req.method = "GET"; req.headers = { "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36 Edg/87.0.664.55" }; const res = await req.loadString(); // console.log(res) const tmp = res.split(`<textarea id="hotsearch_data" style="display:none;">`)[1].split(`</textarea>`)[0] const data = JSON.parse(tmp).hotsearch; for(let i of data.slice(0,10)){ const stack = w.addStack() stack.centerAlignContent() const text = stack.addText(i.pure_title) text.textColor = Color.white() text.font = Font.semiboldSystemFont(9) } if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- 获取中国北京(或者其他地方则需要到各省的政务网站去查看了)一周尾号限行
- 这里引入了一个名为no-background的脚本,需要提前先写好放置在Scriptable中,先运行该透明背景脚本,将手机桌面截屏的图片保存好,运行脚本后将该图片设为背景,之后就可以运行实现Scriptable小组件的透明化布置了,no-background脚本请参考该网址:https://github.com/supermamon/scriptable-no-background,当然,你也可以自己设置背景
const { transparent } = importModule('no-background') //自己设置时该行去掉 const w = new ListWidget() w.backgroundImage = await transparent(Script.name()) //await transparent(Script.name())替换为new Color("#ff0000")等 let url = "http://yw.jtgl.beijing.gov.cn/jgjxx/services/getRuleWithWeek" let req = new Request(url) let result = await req.loadJSON() const stack = w.addStack(); stack.centerAlignContent(); const title = stack.addText("北京一周尾号限行") title.textColor = Color.white() title.font = Font.semiboldSystemFont(12) if(result.state="success"){ //获取数据成功,进行筛选 let tmp = result.result; for(let i = 0; i<tmp.length; i++){ const stack = w.addStack(); stack.centerAlignContent(); const days = stack.addText(tmp[i].limitedWeek) days.textColor = Color.white() days.font = Font.semiboldSystemFont(12) stack.addSpacer(20) const text = stack.addText(tmp[i].limitedNumber) text.textColor = Color.white() text.font = Font.semiboldSystemFont(12) stack.addSpacer(20) } }else{ //获取数据失败 } if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- 获取豆瓣电影一周口碑榜
- 这里说一下,需要先以WebView.loadURL打开豆瓣官网,切换到电脑模式后再返回就可以获取了,代码中有一段注释“//”去掉就可以了,当切换到电脑端界面时返回再注释上就可以了
const w = new ListWidget() w.backgroundColor = new Color("#42a056") const wv = new WebView(); const url = 'https://movie.douban.com/'; await wv.loadURL(url) var getData = ` function getData(){ a = [] x = document.querySelectorAll(".billboard-bd .title a") for(s of x){ a.push(s.innerHTML) } return a } getData() ` let response = await wv.evaluateJavaScript(getData,false) for(let r in response){ const stack = w.addStack() stack.centerAlignContent() const text = stack.addText(response[r]) text.textColor = Color.white() stack.addSpacer(10) } if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } // WebView.loadURL(url) Script.setWidget(w) Script.complete()
- 订阅或者重要日期进程条
- 设置一个重要日期或者服务的到期时间,与今天时间对比,距离截止小于60天进度条变成红色以提醒
const width=125 const h=5 const w = new ListWidget() w.backgroundColor=new Color("#222222") //设置截止列表 const arr = { 'Bandwagonhost': '2021-12-08', 'GoDaddy': '2022-06-15', 'Textnow': '2022-04-09', 'Name': '2029-10-11' } const oneDay = 24 * 60 * 60 * 1000; for(let i of Object.keys(arr)){ const firstDate = new Date(`${arr[i]}`); const secondDate = new Date(); const diffDays = Math.round(Math.abs((firstDate - secondDate) / oneDay)); if(diffDays<=60){ getwidget(diffDays + 30, 31, i, "#ff0000") }else{ getwidget(diffDays + 30, 31, i, "#ffd60a") } } Script.setWidget(w) Script.complete() w.presentSmall() function getwidget(ends, haveGone, str, color) { const titlew = w.addText(str) titlew.textColor = new Color("#e587ce") titlew.font = Font.boldSystemFont(13) w.addSpacer(6) const imgw = w.addImage(creatProgress(ends, haveGone, color)) imgw.imageSize=new Size(width, h) w.addSpacer(6) } function creatProgress(ends, havegone, color){ const context =new DrawContext() context.size=new Size(width, h) context.opaque=false context.respectScreenScale=true context.setFillColor(new Color("#48484b")) const path = new Path() path.addRoundedRect(new Rect(0, 0, width, h), 3, 2) context.addPath(path) context.fillPath() context.setFillColor(new Color(color)) const path1 = new Path() path1.addRoundedRect(new Rect(0, 0, width*havegone/ends, h), 3, 2) context.addPath(path1) context.fillPath() return context.getImage() }
- 汇率展示
- 这个需要到一个网址申请api,app.exchangerate-api.com,每月有1500次请求限制而已,将代码中的url换上自己的即可
const w = new ListWidget() w.backgroundColor = new Color("#222222") const resp= await get({url:"https://v6.exchangerate-api.com/v6/your token/latest/USD"}) const arr = Object.keys(resp["conversion_rates"]).filter(x=>x=="USD" || x=="CNY" || x=="JPY") for(let rate of arr){ const stack =w.addStack() stack.centerAlignContent() const img= await loadImage("https://www.ecb.europa.eu/shared/img/flags/"+rate+".gif") const imgw =stack.addImage(img) imgw.imageSize=new Size(20, 20) stack.addSpacer(20) const textw = stack.addText(resp["conversion_rates"][rate].toFixed(2)) textw.textColor = Color.white() stack.addSpacer(20) } Script.setWidget(w) Script.complete() w.presentLarge() async function get(opts) { const request = new Request(opts.url) request.headers = { ...opts.headers, ...this.defaultHeaders } var result=await request.loadJSON() return result } async function loadImage(imgUrl) { let req = new Request(imgUrl) let image = await req.loadImage() return image }
- 微博热搜Top10
- 伯衡君这里设置了两种模式,一种是网页打开模式,一种是小组件模式,可以自由选择,如果选择网页打开模式,那么建议把.slice(0,10)去掉,然后将// WebView.loadHTML(title)前面的“//”去掉
const wv = new WebView() const w = new ListWidget() w.backgroundColor = new Color("#E6162D") const url = "https://m.weibo.cn/api/container/getIndex?containerid=106003type%3D25%26t%3D3%26disable_hot%3D1%26filter_type%3Drealtimehot" const req = new Request(url) const result = await req.loadJSON() const check = result.ok var title = ` <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ background-color:#E6162D; } .weibohot{ color:#000000; } ul li{ list-style:none; } </style> <div calss="weibohot"> <ul> ` if (check != 1) { return } else { for (let i of result.data.cards[0].card_group.slice(0,10)) { const stack = w.addStack() stack.centerAlignContent() const text = stack.addText(i.desc) text.textColor = Color.white() text.font = Font.semiboldSystemFont(9) title += `<li>${i.desc}</li>`; } title += "</ul></div>" } // WebView.loadHTML(title) if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- 实时上证和深成指数
const w = new ListWidget() w.backgroundColor = new Color("#4169E1") const array = ["上证指数", "深圳成指"] let url = "https://cors.luckydesigner.workers.dev/?http://hq.sinajs.cn/list=sh000001,sz399001" let req = new Request(url) let result = await req.loadString() let sz = result.split(";") for(let i = 0; i<sz.length - 1; i++){ const stack = w.addStack() stack.centerAlignContent() const text = stack.addText(array[i]) text.textColor = Color.white() stack.addSpacer(20) const ratew = stack.addText(sz[i].split(",")[3]) ratew.textColor = Color.white() stack.addSpacer(20) } if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- 知乎热榜Top10
const w = new ListWidget() w.backgroundColor = new Color("#4169E1") let url = "https://api.zhihu.com/topstory/hot-lists/total?limit=10&reverse_order=0" let req = new Request(url) let result = await req.loadJSON() for(let i = 0; i<result.data.length; i++){ if(i<10){ const stack = w.addStack() stack.centerAlignContent() const text = stack.addText(`${i + 1}.${result.data[i].target.title}`) text.textColor = Color.white() text.font = Font.semiboldSystemFont(9) } } if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- Nasa美图
- 先前往https://api.nasa.gov/申请一个key,之后替换脚本中的Api key为自己的就可以了
let apiURL = "https://api.nasa.gov/planetary/apod"; let apiKey = "Api Key"; if ( Keychain.contains('Api Key') ) { apiKey = Keychain.get('Api Key'); } let apodNavURL = "https://apod.nasa.gov"; function createWidget() { let widget = new ListWidget(); widget.spacing = 0; let now = Date.now(); let delay = 6 * 60 * 60 * 1000; let nextUpdate = new Date(now + delay); widget.refreshAfterDate = nextUpdate; return widget; } async function loadPhoto(widget) { let requestURI = `${apiURL}?api_key=${apiKey}`; let request = new Request(requestURI); let json = await request.loadJSON(); if ( json && json.url ) { if ( json.media_type == 'image' ) { let imgRequest = new Request(json.url); let img = await imgRequest.loadImage(); widget.backgroundImage = img; } else if ( json.media_type == 'video' && json.url.match(/^https?:\/\/(www.)?youtube.com/) ) { let ytID = json.url.match(/\/embed\/([^)]+)\?.*/, "/vi/$1/0.jpg") let imgURL = `https://img.youtube.com/vi/${ ytID[1] }/0.jpg`; let imgRequest = new Request(imgURL); let img = await imgRequest.loadImage(); widget.backgroundImage = img; let font = Font.blackRoundedSystemFont(10); let text = widget.addText("Video"); text.rightAlignText(); text.font = font; text.textColor = Color.white(); widget.addSpacer(null); } else { let font = Font.blackRoundedSystemFont(22); let text = widget.addText("今天没有天文图像 "+json.media_type); text.centerAlignText(); text.font = font; text.textColor = Color.white(); let bg = new LinearGradient(); bg.colors = [ new Color("#6666ff", 1.0), new Color("#000033", 1.0) ]; bg.locations = [0, 1]; widget.backgroundGradient = bg; } widget.url = apodNavURL; } else if ( json && json.error && json.error.message ) { widget.addText(json.error.message); } else { widget.addText("Error, no message") } } let widget = createWidget(); await loadPhoto(widget); if (config.runsInWidget) { Script.setWidget(widget); } else { widget.presentLarge(); } Script.complete();
- 随机菜谱
- 随机展示一道菜,点击插件后跳转到Youtube观看制作方法
const {meals} = await new Request("https://www.themealdb.com/api/json/v1/1/random.php").loadJSON() const { strMeal: title, strMealThumb: imgUrl, strYoutube: videoUrl, strSource: articleUrl } = meals[0] const w = new ListWidget() w.url = videoUrl || articleUrl w.backgroundColor = new Color("#222222") w.backgroundImage = await new Request(imgUrl).loadImage() const t = w.addText(title) t.textColor = Color.white() t.font = new Font("Avenir-Heavy", 28) t.shadowRadius = 3 t.shadowColor = Color.black() t.centerAlignText() if(Device.isPad()){ w.presentExtraLarge() }else if(Device.isPhone()){ w.presentLarge() } Script.setWidget(w) Script.complete()
- 随机电影台词
- 随机一句经典电影台词
const isLarge = config.widgetFamily === 'large' async function getData() { const target = 'https://howdz.deno.dev/movieLines' const { img1, img2, img3, img4, link, name, quotes } = await (new Request(target)).loadJSON() const randomImgArr = [img1, img1, img2, img3, img4].filter(Boolean) const randomImgIdx = ~~(Math.random() * randomImgArr.length) const randomImg = randomImgArr[randomImgIdx] const wallpaperImg = randomImg.replace('original', 'w1280') const img = await (new Request(wallpaperImg)).loadImage() const greyImg = await getGreyImg(img) return { name, link, quotes, img: greyImg } } async function getGreyImg(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.7)) ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height'])) let res = await ctx.getImage() return res } const widget = new ListWidget() const { name: movieName, link, quotes, img } = await getData() widget.backgroundImage = img widget.url = link const content = widget.addStack() content.setPadding(0, 0, 10, 0) const quotesText = content.addText(quotes) quotesText.lineLimit = isLarge ? 5: 3 quotesText.textColor = Color.white() quotesText.font = Font.boldSystemFont(isLarge ? 24: 18) quotesText.shadowRadius = 2 quotesText.shadowOffset = new Point(1, 1) const footer = widget.addStack() footer.setPadding(isLarge ? 32 : 16, 0, 0, 0) footer.addSpacer() const footerText = footer.addText(`——『 ${movieName} 』`) footerText.lineLimit = 1 footerText.shadowRadius = 2 footerText.shadowOffset = new Point(1, 1) footerText.textColor = Color.white() footerText.rightAlignText() if (config.runsInWidget) { Script.setWidget(widget) } else { widget.presentMedium() } Script.complete()
- 随机古诗
- 随机生成一段古诗
const fontSizeMap = { small: 16, medium: 24, large: 32 } const fontSize = fontSizeMap[config.widgetFamily || 'medium'] const loadImage = async (url) => (await new Request(url)).loadImage() const getGreyImg = async (img, light = 0.4) => { const 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", light)) ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height'])) return await ctx.getImage() } const loadRandomBg = async () => { const randomImgURL = 'https://howdz.deno.dev/unsplash/random?w=768&keyword=Nature' const randomImg = await loadImage(randomImgURL) const greyImg = await getGreyImg(randomImg) return greyImg } const loadVerse = async () => (await new Request('https://v1.jinrishici.com/all.json')).loadJSON() const { content: randomVerse, origin: title }= await loadVerse() const widget = new ListWidget() const randomImg = await loadRandomBg() widget.setPadding(0, fontSize / 2, 0, fontSize / 2) widget.backgroundImage = randomImg widget.url = 'https://hanyu.baidu.com/s?wd=' + encodeURIComponent(title)+ '&from=poem' const textEl = widget.addText(randomVerse) textEl.textColor = Color.white() textEl.font = Font.boldSystemFont(fontSize) textEl.shadowRadius = 2 textEl.shadowOffset = new Point(1, 1) textEl.centerAlignText() if (config.widget !== 'small') { const footer = widget.addStack() footer.setPadding(fontSize, 0, 0, 0) footer.addSpacer() const footerText = footer.addText(`——『 ${title} 』`) footer.font = Font.boldSystemFont(16) footerText.lineLimit = 1 footerText.shadowRadius = 2 footerText.shadowOffset = new Point(1, 1) footerText.textColor = Color.white() footerText.rightAlignText() } if (config.runsInWidget) { Script.setWidget(widget) } else { widget.presentMedium() } Script.complete()
- 随机英文名居
- 随机生成一段英文
const fontSizeMap = { small: 18, medium: 24, large: 32 } const fontSize = fontSizeMap[config.widgetFamily || 'medium'] const loadImage = async (url) => (await new Request(url)).loadImage() const getGreyImg = async (img, light = 0.4) => { const 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", light)) ctx.fillRect(new Rect(0, 0, img.size['width'], img.size['height'])) return await ctx.getImage() } const loadRandomBg = async () => { const randomImgURL = 'https://howdz.deno.dev/unsplash/random?w=768&keyword=Nature' const randomImg = await loadImage(randomImgURL) const greyImg = await getGreyImg(randomImg) return greyImg } const loadSentence = async () => (await new Request('https://favqs.com/api/qotd')).loadJSON() const { quote } = await loadSentence() const { body: text, author } = quote const widget = new ListWidget() const randomImg = await loadRandomBg() widget.setPadding(0, fontSize / 2, 0, fontSize / 2) widget.backgroundImage = randomImg const textEl = widget.addText(text.replaceAll('\\r', '').replaceAll('<br>', '')) textEl.textColor = Color.white() textEl.font = Font.boldSystemFont(fontSize) textEl.shadowRadius = 2 textEl.shadowOffset = new Point(1, 1) textEl.centerAlignText() if (config.widgetFamily !== 'small') { const footer = widget.addStack() footer.setPadding(fontSize, 0, 0, 0) footer.addSpacer() const footerText = footer.addText(`—— ${author}`) footer.font = Font.boldSystemFont(16) footerText.lineLimit = 1 footerText.shadowRadius = 2 footerText.shadowOffset = new Point(1, 1) footerText.textColor = Color.white() footerText.rightAlignText() } if (config.runsInWidget) { Script.setWidget(widget) } else { widget.presentMedium() } Script.complete()
- 我的微信
- 微信扫一扫加好友
- 我的微信公众号
- 扫描关注公众号