Issues for version 1.7.4

There’s a issue happened after I update the version to 1.7.4.
My widget are based on drawcontext module. It becomes very blurry with the latest version.


Have you set respectScreenScale to true on your DrawContext?

Of course, you could see there is no problem on version 1.7.

Would you mind sharing your code?

const files = FileManager.local()  //文件存储位置
const currentDate = new Date()
const apiKey = "i6u8OQd9TznDJJPJ"  //彩云天气api
var lockLocation = true //app内预览设置

// ========
// if 
const argsParam = args.widgetParameter
if (config.runsInWidget== true)
{ 
if (args.widgetParameter == 1)
{
  lockLocation = true  //参数设为1则锁定定位信息
}else{
  lockLocation = false
}}


const locale = "en" //时间显示语言
const useCache = false  //是否使用缓存数据
const newBG = false  //是否使用新的背景图片
const rainycolor = new Color("0283d3", 1) //下雨天温度字体颜色
const numberOfEvents = 2 //显示事件数量
const targetDate = 360 //显示未来多少天内的日历事件

const scale = Device.screenScale()

// 背景调整

const slipPosition = 370
const daystoShow = 6 

// ######数据设定#######
const location = await getLocation()
// log("地点"+location)
const caiyun = await getCaiyunData()
const alertInfo =caiyun.dataToday.result.alert.content[0]
// log ("预警信息:"+alertInfo)
const dailyTemperature = caiyun.dataToday.result.daily.temperature
const rainIndex = caiyun.dataToday.result.hourly.precipitation
const comfortindex = caiyun.dataToday.result.realtime.life_index.comfort.index
const feelslikeT = Math.round(caiyun.dataToday.result.realtime.temperature)
const currentTemperature = feelslikeT+"°"
const feeling = caiyun.dataToday.result.realtime.life_index.comfort.desc
const realtimeweather = caiyun.dataToday.result.realtime.skycon
const todaysunrise = caiyun.dataToday.result.daily.astro[0].sunrise.time.slice(0, 2)
const todaysunset = caiyun.dataToday.result.daily.astro[0].sunset.time.slice(0, 2)
const data = caiyun.dataToday.result.hourly.temperature
const dailydata = caiyun.dataToday.result.daily.temperature
const Mainweather = caiyun.dataToday.result.daily.skycon
const USAQI = caiyun.dataToday.result.realtime.air_quality.aqi.usa.toString()
// const AirCon=caiyun.dataToday.result.realtime.air_quality.description.usa
// .toString()
log("AQI"+USAQI)
const probabilityOfRain = caiyun.dataToday.result.minutely.probability
// log(probabilityOfRain)
const maxProbability = (Math.max(probabilityOfRain[0],probabilityOfRain[1],probabilityOfRain[2],probabilityOfRain[3])*100).toString().slice(0, 2)+"%"

const addressName = await getAddress()

const WeatherDesc = caiyun.dataToday.result.hourly.description;

const updateTimeString = caiyun.dataToday.server_time
// log (caiyun.dataToday.result)


// ######
const linecolor = new Color("cec403", 1)
// const rectcolor = new Color("483565", 0.6)
const rectcolor = new Color("675d81", 1)
const fontcolor = new Color("888888", 1)
const shadowColor = new Color("333333",1)
const halfwhite = new Color("ffffff", 0.5)
const lightwhite = new Color("ffffff", 0.04)
const lightTextColor = new Color("e0e0e0", 1)
const white = new Color("ffffff", 1)

// ######字体设置#######

const widget = new ListWidget()
widget.setPadding(0, 0, 0, 0)

// ………………背景选择………………
const path = files.joinPath(files.documentsDirectory(), "CYTQRL")
    if (newBG && config.runsInApp)
{
  const img = await Photos.fromLibrary()
      widget.backgroundImage = Addblur(img)
      files.writeImage(path, img)
}else{
    if (config.runsInWidget) { 
      try {
    widget.backgroundImage = Addblur(files.readImage(path))
    log("读取图片成功")
} catch (e){
  widget.backgroundColor = new Color("000000", 1)
  log(e.message)
}    

    } else {
      try {
    widget.backgroundImage = Addblur( files.readImage(path))
    log("读取图片成功")
} catch (e){
      const img = await Photos.fromLibrary()
      widget.backgroundImage = Addblur(img)
      files.writeImage(path, img)
      log(e.message)
    }
  }
}
// …………………………………………
const topDrawing = new DrawContext()
topDrawing.size = new Size(400, 160)
topDrawing.opaque = false
topDrawing.respectScreenScale=true

//  fillRect(topDrawing, 0, 0, 400, 160, 20, new Color("ffffff", 0.1))
// fillRect(topDrawing, 0, 0, 200, 160, 20, new Color("ffffff", 0.1))

for (i=0;i<20;i++)
{
drawLine(topDrawing,200,i*3,200,160-i*3,lightwhite,0.1*i+1)
}


// ######日期显示#######
const df = new DateFormatter()
df.locale=locale
const date = currentDate


var AQIcolor
var AirCon
if (USAQI<= 50)
{ AQIcolor = new Color("1B5E20",0.6)
AirCon = "优"
}
else if (USAQI<=100)
{ AQIcolor = new Color("D3C407",1)
AirCon = "良"
}
else if (USAQI<=150)
{ AQIcolor = new Color("CB904a",1)
AirCon = "轻度污染"
}
else if (USAQI<=200)
{ AQIcolor = new Color("ff0000",0.6)
AirCon = "中度污染"
}
else if (USAQI<=300)
{ AQIcolor = new Color("ba0033",0.6)
AirCon = "重度污染"
}
else
{ AQIcolor = new Color("7e0023",0.6)
AirCon = "严重污染"
}


// 当前天气// 

drawIcon(topDrawing,5, 43, realtimeweather,60)
// 当前温度
drawText(topDrawing,90,53, 110,80,
currentTemperature, white, "a",45,"center")
drawText(topDrawing,0,120, 200,80,
"AQI "+USAQI, white, "a",22,"center")
// 空气质量&下雨概率

var textColortoShow
if(USAQI<=100)
{textColortoShow = fontcolor}
else {textColortoShow = lightTextColor}


// 温度条位置
var tempHeight
if (feelslikeT < Math.round(dailyTemperature[0].min))
{ tempHeight = 12 }
if (feelslikeT > Math.round(dailyTemperature[0].max))
{ tempHeight = 80 }
if (feelslikeT >= Math.round(dailyTemperature[0].min) && feelslikeT <= Math.round(dailyTemperature[0].max))
{ tempHeight = (feelslikeT-Math.round(dailyTemperature[0].min))*68/(Math.round(dailyTemperature[0].max)-Math.round(dailyTemperature[0].min))+12 }

// ######温度条#######// // 
// // fillRect(topDrawing,15,35, 12, 80, 6, lightwhite)
// fillRect(topDrawing,15,115-tempHeight, 12, tempHeight, 6, white)


// ####每日预报########// // 

for (i=0;i<3;i++){
//   log(Mainweather[i].value)
  
// 图标
drawIcon(topDrawing,197/12*(i*4+1)+200,
45,Mainweather[i].value,30)


// 每日温度
let fullDayliyTemperatureData = Math.round(dailydata[i].max)+"º / "+Math.round(dailydata[i].min)+"º"

drawText(topDrawing, 197/3*i+1+203,90, 197/3,30,Math.round(dailydata[i].max)+"º",white,"semibold",24,"center")

drawText(topDrawing, 197/3*i+1+203,120, 197/3,30,Math.round(dailydata[i].min)+"º",halfwhite,"semibold",24,"center")
// 每日日期
const weatherDate = new Date()
weatherDate.setDate(weatherDate.getDate() + i)
log(weatherDate)
df.dateFormat = "E."
// drawText(topDrawing, 197/3*i+1+203,10, 197/3,30,df.string(weatherDate),white,"a",22,"center")
}
drawText(topDrawing, 203,10, 197/3,30,"今",halfwhite,"a",24,"center")
drawText(topDrawing, 203+197/3,10, 197/3,30,"明",halfwhite,"a",24,"center")
drawText(topDrawing, 203+197/3*2,10, 197/3,30,"后",halfwhite,"a",24,"center")
// 
// 底部信息
var content
var alertTextColor
log (alertInfo)

var locationName
  if (lockLocation == true)
  {locationName = "♡ "+addressName}
  else {locationName = "ꚰ "+addressName}
  
  if (alertInfo == undefined)
  {
    content = locationName
//   content = providepoetry.data.content+"    ⊙"+providepoetry.data.origin.author+"⊙"
}else{
  content = "⚠️"+"天气预警"
  }
  drawText(topDrawing, 0, 10, 200, 30, content, lightTextColor,"a",22,"center")

// ############
df.dateFormat = "HH:mm"
let updateTime = df.string(new Date(updateTimeString*1000))
//  drawText(topDrawing,0, 120, 200, 30, "↻ "+updateTime+"",lightTextColor,"a",20,"center")

const topStack = widget.addStack()
// // 
// topStack.size = new Size(1014/3, 474/3)
topStack.addImage(topDrawing.getImage())


Script.setWidget(widget)
widget.presentMedium()
Script.complete()


// =====线框======
function drawBackground()
{
  let bacdrawing = new DrawContext()
  bacdrawing.opaque = false
  bacdrawing.size = musicStack.size
  log(bacdrawing.size)
let rect = new Rect(10, 1, musicStack.size.width-21, musicStack.size.height-2)
let path = new Path()
path.addRoundedRect(rect, musicStack.size.height/2-1, musicStack.size.height/2-1)
log(rect.height)
bacdrawing.setStrokeColor(new Color("333333", 0.2))
bacdrawing.setLineWidth(1)
bacdrawing.addPath(path)
bacdrawing.strokePath()

drawPoint(bacdrawing,9.5,0.5,new Color("555555", 0.35),musicStack.size.height-1)

return bacdrawing.getImage()

}

// ######画线######

function drawLine(drawing,x1,y1,x2,y2,color,width)
{
  const path = new Path()
  path.move(new Point(Math.round(x1),Math.round(y1)))
  path.addLine(new Point(Math.round(x2),Math.round(y2)))
  
  drawing.addPath(path)
  drawing.setStrokeColor(color)
  drawing.setLineWidth(width)
  drawing.strokePath() 
}

// ######绘制文字#######
function drawText(drawing, x, y, width,height,text,color,font,fontsize,alignment)
  {
    if (font=="a"){
drawing.setFont(Font.boldSystemFont(fontsize))}
if (font=="default"){
drawing.setFont(Font.lightMonospacedSystemFont(fontsize))}
if (font=="semibold"){
drawing.setFont(Font.semiboldSystemFont(fontsize))}
  drawing.setTextColor(color)
  if(alignment == "left")
  {drawing.setTextAlignedLeft()}
  if(alignment == "center")
  {drawing.setTextAlignedCenter()}
  if(alignment == "right")
  {drawing.setTextAlignedRight()}
  drawing.drawTextInRect(text, new Rect(x, y, width, height))

}

// ######绘制主要天气图标#######
function drawIcon(drawing,x1,y1,WeatherCondition,size)
{
if (y1>50)
{
log(drawing)
// if (y1>50)
if (WeatherCondition=="CLOUDY")
  {y1=y1+1.7}
  if (WeatherCondition=="PARTLY_CLOUDY_DAY")
  {y1=y1-1}
  if(WeatherCondition=="LIGHT_RAIN"||
  WeatherCondition=="MODERATE_RAIN"||
  WeatherCondition=="HEAVY_RAIN"||
  WeatherCondition=="STORM_RAIN"
  )
  {y1=y1+1.2}
  }
  else
  {
    if (WeatherCondition=="CLOUDY")
  {y1=y1+5}
  }
drawing.drawImageAtPoint(provideSymbol(WeatherCondition, 0,size), new Point(x1, y1))  
}

// ######提供天气图标名称#######
function provideSymbol(cond,night,size) {
//   log("字体大小"+size)
  let symbols = {
    
"CLEAR_DAY": function() {return"sun.max.fill"},
"CLEAR_NIGHT": function() {return"moon.stars.fill"},
"PARTLY_CLOUDY_DAY": function() {return"cloud.sun.fill"},
"PARTLY_CLOUDY_NIGHT": function() {return"cloud.moon.fill"},
"CLOUDY": function() {return"cloud.fill"},
"LIGHT_HAZE": function() {return night? "cloud.fog.fill":"sun.haze.fill"},
"MODERATE_HAZE": function() {return night? "cloud.fog.fill":"sun.haze.fill"},
"HEAVY_HAZE": function() {return night? "cloud.fog.fill":"sun.haze.fill"},
"LIGHT_RAIN": function() {return"cloud.drizzle.fill"},
"MODERATE_RAIN": function() {return"cloud.rain.fill"},
"HEAVY_RAIN": function() {return"cloud.rain.fill"},
"STORM_RAIN": function() {return"cloud.heavyrain.fill"},
"FOG": function() {return"cloud.fog.fill"},
"LIGHT_SNOW": function() {return"cloud.sleet.fill"},
"MODERATE_SNOW": function() {return"cloud.snow.fill"},
"HEAVY_SNOW": function() {return"cloud.snow.fill"},
"STORM_SNOW": function() {return"snow"},
"DUST": function() {return night? "cloud.fog.fill":"sun.dust.fill"},
"SAND": function() {return night? "cloud.fog.fill":"sun.dust.fill"},
"WIND": function() {return"wind"},
"AirCon":function(){return"leaf.fill"},
    
  }
  let sfs = SFSymbol.named(symbols[cond]())
  sfs.applyFont(Font.systemFont(size))

return sfs.image
}
// ========绘制图标图像============
async function tintSFSymbol(image, color) {
  let html = `
  <img id="image" src="data:image/png;base64,${Data.fromPNG(image).toBase64String()}" />
  <canvas id="canvas"></canvas>
  `;
  
  let js = `
    let img = document.getElementById("image");
    let canvas = document.getElementById("canvas");
    let color = 0x${color.hex};

    canvas.width = img.width;
    canvas.height = img.height;
    let ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    let imgData = ctx.getImageData(0, 0, img.width, img.height);
    // ordered in RGBA format
    let data = imgData.data;
    for (let i = 0; i < data.length; i++) {
      // skip alpha channel
      if (i % 4 === 3) continue;
      // bit shift the color value to get the correct channel
      data[i] = (color >> (2 - i % 4) * 8) & 0xFF
    }
    ctx.putImageData(imgData, 0, 0);
    canvas.toDataURL("image/png").replace(/^data:image\\/png;base64,/, "");
  `;
  
  let wv = new WebView();
  await wv.loadHTML(html);
  let base64 = await wv.evaluateJavaScript(js);
  return Image.fromData(Data.fromBase64String(base64));
}



function drawSfs (drawing,x1,y1,symblos,size)
{
let sfs = SFSymbol.named(symblos)
sfs.applyBoldWeight()
sfs.tintColor = new Color("ffffff", 1)
sfs.applyFont(Font.systemFont(size))

let a = drawing.drawImageAtPoint(sfs.image, new Point(x1, y1))  
}

//====== get style font from internet ======
async function getFont (workday,color,fontSize){
const fontUrl = "http://d.xiazaiziti.com/en_fonts/fonts/k/Kaushan-Script.ttf";
const js = `
const myFont = new FontFace("myFont","url(${fontUrl})");
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width=200
canvas.heigth=60
myFont.load().then(font => {
    document.fonts.add(font);
    ctx.font = "${fontSize}px myFont";
    ctx.fillStyle = ${color};
    ctx.fillText(${workday}, 0, 60);
    completion(canvas.toDataURL());
})
null
`
const webView=new WebView();
const returnValue = await webView.evaluateJavaScript(js, true);
const imageDataString = returnValue.slice(22);
const imageData = Data.fromBase64String(imageDataString);
const imageFromData = Image.fromData(imageData);
return imageFromData;
}


// ======================
function fillRect (drawing,x,y,width,height,cornerradio,color)
{
let path = new Path()
let rect = new Rect(x, y, width, height)
path.addRoundedRect(rect, cornerradio, cornerradio)
drawing.addPath(path)
drawing.setFillColor(color)
drawing.fillPath()
}


function drawPoint(drawing,x1,y1,color,diaofPoint)
{
let currPath = new Path()
  currPath.addEllipse(new Rect(x1, y1, diaofPoint, diaofPoint))
  drawing.addPath(currPath)
  drawing.setFillColor(color)
  drawing.fillPath()
}

// ######获取彩云天气数据#######
async function getCaiyunData()
  {
// 设定天气数据缓存路径
const locaData = JSON.parse(location)
const cachePath = files.joinPath(files.documentsDirectory(), "CYTQ-Pih")
const cacheExists = files.fileExists(cachePath)
const cacheDate = cacheExists ? files.modificationDate(cachePath) : 0
var data
// 假设存储器已经存在且距离上次请求时间不足60秒,使用存储器数据
if (cacheExists && (currentDate.getTime() - cacheDate.getTime()) < 60000) {
  const cache = files.readString(cachePath)
  data = JSON.parse(cache)
  log("==>请求时间间隔过小,使用缓存数据")

// 否则利用 api 得到新的数据
} else {
 
try {
const weatherReq = "https://api.caiyunapp.com/v2.5/"+ apiKey +"/"+locaData.longitude+","+locaData.latitude + "/weather.json?alert=true&dailysteps=7&lang=zh_CN"
log (weatherReq)
const dataToday = await new Request(weatherReq).loadJSON()
// log(weatherReq)
  data = {dataToday}
  files.writeString(cachePath, JSON.stringify(data))
  log("==>天气信息请求成功")}
  catch(e){
  data = JSON.parse(files.readString(cachePath))
  log("==>天气信息请求失败,使用缓存数据/"+e.message)}
}
// log(weatherReq)
return data
}

// ######获取定位信息#######
async function getLocation()
{
  // 设定位置缓存数据路径
const locationPath = files.joinPath(files.documentsDirectory(), "DZ-Pih")
// var latitude, longitude
var locationString
// 如果位置设定保存且锁定了,从缓存文件读取信息

if (lockLocation && files.fileExists(locationPath)) {
locationString = files.readString(locationPath)
log("位置锁定,使用缓存数据"+locationString)
// return locationString
// 否则,从系统获取位置信息
} else {
  try {
  let location = await Location.current()
//   latitude = location.latitude
//   longitude = location.longitude
//   locationString = JSON.stringify(location)
//   longitude+","+latitude
locationString = JSON.stringify(location)
  files.writeString(locationPath, locationString)
  log("==>定位成功"+locationString)}
  catch(e){
  locationString = files.readString(locationPath)
  log("==>无法定位,使用缓存定位数据")}
// location = location

//   return locationString
}
// log("地址"+locationString)
return locationString
}


// 未来事件
function enumerateEvents() {
  let futureEvents = []
  for (const event of allEvents) {
    if (event.endDate.getTime() > currentDate.getTime() && !event.title.startsWith("Canceled:")) {
      futureEvents.push(event)
    }
  }
  return futureEvents
}

// #####获取今日诗词######
async function poetry()
  {
const poetryCachePath = files.joinPath(files.documentsDirectory(), "Caiyunweather-Pih")
var poetryData
try {
poetryData = await new Request("https://v2.jinrishici.com/sentence").loadJSON()
files.writeString(poetryCachePath, JSON.stringify(poetryData))
log("==>诗歌获取成功")
} catch(e){
  poetryData = JSON.parse(files.readString(poetryCachePath))
  log("==>获取诗歌失败,使用缓存数据")
}

return poetryData
}



function Addblur (Img)
{
  const drawing = new DrawContext()
  drawing.size = Img.size
  const rect = new Rect(0, 0, drawing.size.width, drawing.size.height)
  drawing.drawImageInRect(Img, rect)
  drawing.setFillColor(new Color("000000", 0))
  drawing.fillRect(rect)
  drawing.setStrokeColor(new Color("ffffff", 0))

//   for (i=0;i<5;i++)
// {drawwhiteline(slipPosition+i*1.5, 70-i, 610+i*1.5, 70-i, AQIcolor, 60/(40+15*i)),drawing}
drawing.setLineWidth(4)
// drawing.strokeRect(rect)

  let blurImg = drawing.getImage()
return blurImg
}


async function provideImg ()
{
 const ImgCache = files.joinPath(files.documentsDirectory(),`WYImg`)
const cacheExists = files.fileExists(ImgCache)
const cacheDate = cacheExists ? files.modificationDate(ImgCache) : 0
// =========
var ImgUrl

if (!useCache)
{ 
try { 
const playListDetails = "https://api.uomg.com/api/rand.music&format=json"
// let ImgUrl = (await new Request(playListDetails).loadJSON()).data.picurl

while (!ImgUrl)
{ImgUrl = (await new Request(playListDetails).loadJSON()).data.picurl
files.writeString(ImgCache , ImgUrl)
}
}
catch(e) { ImgUrl = files.readString(ImgCache)}
}

if(useCache)
{ImgUrl = files.readString(ImgCache)
log ("使用缓存"+ImgUrl)}
const Img = await new Request(ImgUrl).loadImage()

  const drawing = new DrawContext()
  drawing.size = Img.size
  const rect = new Rect(0, 0, drawing.size.width, drawing.size.height)
  drawing.drawImageInRect(Img, rect)
  drawing.setFillColor(new Color("000000", 0.25))
  drawing.fillRect(rect)
  let blurImg = drawing.getImage()
return blurImg
}


function centerAlign (image,text, stack)
{
  stack.layoutVertically()
  stack.addSpacer()
let IconStack = stack.addStack()
IconStack.layoutHorizontally()
IconStack.addSpacer()
let Icon = IconStack.addImage(image)
Icon.rightAlignImage()
Icon.imageSize = new Size(16, 16)
Icon.tintColor = Color.white()
IconStack.addSpacer()
let textStack = stack.addStack()
textStack.layoutHorizontally()
textStack.addSpacer()
let Text = textStack.addText(text)
Text.centerAlignText()
Text.textColor = Color.white()
Text.font = new Font("heavymenlo", 7)
textStack.addSpacer()
stack.addSpacer()
}

async function getAddress()
{
// 设定位置缓存数据路径
const locaData = JSON.parse(location)
var address
var locationName = files.joinPath(files.documentsDirectory(), "DIZHI")
try {
const addressUrl = "https://api.map.baidu.com/geocoder?location="+locaData.latitude+","+locaData.longitude+"&output=json"
log("地址:"+addressUrl)

address = await new Request(addressUrl).loadJSON()
address = address.result.addressComponent.district
  files.writeString(locationName, JSON.stringify(address))
  log("==>地址定位成功")}
  catch(e){
  address = JSON.parse(files.readString(locationName))
  log("==>地址无法定位,使用缓存定位数据")}

return address
}
1 Like