=INDEX(A:A,CEILING(ROW(E4)/3,1),1)
conda channel的镜像设置(设置可用清华源)
文章转自:https://blog.csdn.net/weixin_39278265/article/details/84782550
前言
今天在下载OpenCV的时候发现清华的conda channel镜像已经不能用了,故在此记录:
1)如何显示所有channel;
2)如何更换channel。
1 显示所有channel
首先,conda config –show能够显示出所有conda的config信息。
如果我们只想看channels的信息,输入conda config –show channels即可,如下:
(base) C:\Users\dehen>conda config --show channels
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
- defaults
现在我们有两个源,一个清华镜像,一个defauls默认下载源。
然而这个清华源已经不能用了,我在下载opencv的时候,输入:conda install opencv
,但是报错:
(base) C:\Users\dehen>conda install opencv
Solving environment: failed
CondaHTTPError: HTTP 000 CONNECTION FAILED for url https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/win-64/repodata.json
Elapsed: –
An HTTP error occurred when trying to retrieve this URL.
HTTP errors are often intermittent, and a simple retry will get you on your way.
ConnectionError(ReadTimeoutError(“HTTPSConnectionPool(host=’mirrors.tuna.tsinghua.edu.cn’, port=443): Read timed out.”))
所以我打算删除这个源。
2 移除清华镜像
输入:conda config –remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
这个命令是为了移除之前conda config –show channels显示的清华源。
(base) C:\Users\dehen>conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
(base) C:\Users\dehen>conda config --show channels
channels:
- defaults
(base) C:\Users\dehen>
这时候再运行conda config –show channels会发现清华源已经被删除了
3 添加可用的清华源
参考[4],我发现自己之前安装的清华源的地址https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/ 是清华维护的conda三方源之一conda-forge的镜像,,,此外清华还有很多可用的源。
所以我根据官网指示进行了添加:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes
conda config –set show_channel_urls yes的意思是从channel中安装包时显示channel的url,这样就可以知道包的安装来源了。
4 下载opencv
在添加好这个源(channel)之后,我重新运行conda install opencv,果然能够成功下载!!!
但是中间竟然是自动安装,在安装前的确认[Y/N]的时候,conda直接跳过了,默认是Y。。。
这让我非常难受,所以我运行了:
conda config –set always_yes false
(这里的意思是安装确认中,不默认yes,而是由我来决定)
5 一些其他的conda指令
conda install <包名> 安装指定包
conda remove <包名> 移除指定包
conda update <包名> 更新指定包
参考文献
[1] Annaconda 增加删除镜像 channel, 以及其他python包安装解决办法 https://blog.csdn.net/mtj66/article/details/57074986
最开始是看到这里的评论,发现清华的源用不了。
[2] conda常用命令详解 https://www.jianshu.com/p/484cfbf45ed3
在这里学到了一些conda的命令
[3] [加速]为conda添加国内镜像 https://www.aliyun.com/jiaocheng/459144.html
在这里发现原来我之前添加的清华源,和真正的清华源不一样,这促使我进一步寻找可用的清华源。
[4] Anaconda 镜像使用帮助 https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/
这里是清华源的官网,我找到了可用的清华源并进行了安装。
Mysql数据转存
E:\SERVER\qampp\mysql\bin\mysqldump.exe –skip-lock-tables –no-create-info –add-drop-table –extended-insert –events -u root –host=localhost –port=3306 dbname
MySQL 全文索引 实现相似度搜索
模糊查询
Mysql实现模糊查询 最简单的是LIKE
关键字, 如
SELECT * FROM `content` WHERE `topic` LIKE '%地球%';
而当然也可以使用LOCATE()
,POSITION()
等内置函数来实现. 不过 这种模糊查询都存在一定的局限性. 举个🌰:
记录为: 你好,我的世界
, 此时通过关键词你好世界
便无法搜索到.
如何解决
在Mysql 5.7.6后 Mysql内置了ngram分词疫情, 可以实现中文, 日文, 韩文的解析. 我们需要对指定字段建立全文索引并指定分词引擎.
- 需要注意: 建立全文索引的字段 数据类型只能为 VARCHAR, TEXT, CHAR
设置分词
我们需要先设置ngram的分词长度, 由于中文词语一般为两个字, 所以建议设置为2
mysqld --ngram_token_size=2
也可以通过修改mysql配置文件, 指定
ngram_token_size=2
建立索引
选定分词引擎建立FLULTEXT索引
ALTER TABLE `table_name` ADD FULLTEXT INDEX `index_name`(`column_name`) WITH PARSER ngram;
进行搜索
建立索引后, 可以通过 match against语句进行搜索
SELECT * FROM `table` WHERE MATCH (`column`) against('你好世界')
全文索引的两种搜索模式
- 自然语言搜索(也是默认的搜索模式): 不能使用操作符 进行复杂检索.
- BOOLEAN模式 : 可以通过操作符 进行复杂搜索, 与搜索引擎类似.
SELECT * FROM `table` WHERE MATCH (`column`) against('你好世界' IN NATURAL LANGUAGE MODE)
# 必须包含'你好', 但不能包含'我的'
SELECT * FROM `table` WHERE MATCH (`column`) against('+你好 -我的' IN BOOLEAN MODE)
参考文章
yii2记数,自动累加
文章一般都有统计浏览次数的需求,一般小型项目的做法就是直接 update 数据库中的某个字段。在 Yii 中怎么实现呢?请往下看:
实现
方式一
Yii2 中有这个 updateAllCounters
静态方法,这种方式是最快并且最省事的实现方式,代码示例如下:
Topic::updateAllCounters(['view_count' => 1], ['id' => $id]);// 实现的效果就是 view_count + 1,1根据你的需求可以是正数也可以是负数。
如果你的条件比较复杂你可以这样写:
Topic::updateAllCounters(['view_count' => 1], ['and', ['xxx' => 0, 'yyy' => 2], ['>', 'zzz', $time]);
如果你现在的需求是:更新一个字段的值,一个字段的值 +1,你可以试着用这种方式去实现:
Topic::updateAll( ['view_count' => new Expression('`view_count` + 1'), 'updated_at' => time()], ['id' => $id]);
PS:此处的 view_count
字段默认值切记不能设置为 null
。
Expression
是表达式的意思,可以实现更多特殊 SQL,想了解更多,可以去搜索一下文档。
方式二
当然你还可以这样去实现:
$model = Post::findOne($id);$model->updateCounters(['view_count' => 1]);
手把手带你撸瀑布流布局的5种实现方式
本文转自:https://www.imooc.com/article/289809
最近项目中需要处理与图片相关的布局,不得不说图片这玩意真想要得到完美的展示效果还真是要费些力气。因为图片的尺寸或者比例各不相同。所以想要不同尺寸的图片有好的显示效果,你就需要找到适合的方式。
而且图片往往是不可或缺元素。毕竟一图胜千言,有时候图片能给带来非常好的效果。
比如我们每天都会使用的表情包,它往往能够表达出我们无法用文字描述的信息,还比如我们经常在公众号里看到的漫画虽然短短几个字,但是却能够让我们看的不亦乐乎。
当然如果我们做图片网站的,那图片的处理就是绕不开的话题了。因对图片的处理经验不多,所以就边学边用。今天就把最近学习与图片相关的知识整理出来。
比如单个图片如何更好的展示,瀑布流布局都有哪些你不知道的实现方式。
接下来我们就直接进入正题,我们先从单张图片的展示说起。
设置宽或高 100%
因为图片其本身的独特性:
- 不设置宽高的情况下会按原有的尺寸显示在网页中。即有多大,显示多大。
- 在非等比缩放的情况下会被拉伸变形。
- 设置宽度或者高度时,会保持原宽高比进行缩放。
<style lang="scss" scoped>
.box1 {
width: 150px;
height: 150px;
border: 2px solid red;
}
.box2 {
width: 150px;
height: 100px;
border: 2px solid red;
img {
display: block;
width: 100%;
height: 100%;
}
}
</style>
显然当我们采用 1、2 种方式的时候破坏性很强,无法应用到实际的项目中去。
所以往往我们会在项目中使用第 3 种方式,即设置高度或者宽度。它会保持原有比例进行缩放。
<style lang="scss" scoped>
.box {
width: 150px;
height: 150px;
border: 2px solid red;
}
.img1 {
width: 100%;
}
.img2 {
height: 100%;
}
</style>
但是问题又来了,图片要么超出容器,要么就会留有空白,除非容器的宽高比恰好等于图片的宽高比时,才会完全贴合。
对于超出容器的图片我们可以使用 overflow: hidden
把超出部分隐藏。图片得到了好的展示效果。但相应的我们也损失了图片的一部分可视区域。
所以这个时候就需要你根据需求进行取舍了,到底是选择隐藏图片的一部分,还是留有空白。有的小伙伴会说,我们产品说了,图片变形没问题,你就给我充满容器就行了。好吧…
即使如此,你也要把这篇文章好好读一读,因为需求是千变万化的,保不齐哪一天就需要了。
又有小伙伴说,这 2 种都不符合我们的产品需求怎么办,还有其他的方式吗?答案是必须的,一起来看。
object-fit
CSS3 的 object-fit 属性是用来指定「可替换元素」的内容是如何适应到容器中的。它的值有 5 种。分别为:fill | contain | cover | none | scale-down
。先看下效果在来一一解释它们到底都是什么意思。
<template>
<div class="box">
<img src="https://picsum.photos/id/1027/200/300"/>
</div>
</template>
<style lang="scss" scoped>
.box {
width: 150px;
height: 150px;
border: 2px solid red;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
}
</style>
看到上面的显示效果,理解起来并不难了。
- fill:会充满整个容器,不考虑宽高比,所以会被拉伸变形。
- contain:会缩放到容器内,保持宽高比。
- cover:会保持比例进行缩放,内容的尺寸一定会大于等于容器,然后进行裁剪。
- none:保持图片的原始尺寸。
而 scale-down
有两种表现方式所以我们单独来看。
- scale-down:会在 none 或 contain 中选择一个,原则是:当容器小时,它的表现和 contain 一样;当图片小时,它的表现和 none 一样。即谁小选择谁。
到这里不知道有没有小伙伴和我一样,在看到图片的不同表现时,我特意去浏览器查看了下 <img>
的真实尺寸,发现依然是 width: 100%;height: 100%;
是充满整个容器的。
但为什么内容显示却有不同的效果呢,这让我产生了疑惑。本着发现探索的精神,就去寻找答案了。
W3c 是这么描述的:<img>
标签创建的是被引用图像的占位空间。
而张鑫旭大大在半深入理解CSS3 object-position/object-fit属性一文中也指出:
<img>
元素其实与内容是相互独立的。<img>
相当于一个外壳包裹着内容。你控制的只是<img>
元素的尺寸。而内容的尺寸则由 object-fit
属性控制。
综上索述,<img>
是一个空间占位符,并不控制内容。原来如此。感觉自己又进步了。每一次的探索,都会发现新的东西,这种感觉很奇妙。特别是还把它整理出文章,提供大家学习,感觉牛逼哄哄带闪电。
知道了这些之后我们操控图片时更加的得心应手一些。那会了这些就结束了吗?不不不,这才是刚刚开始,精彩的还在后面。后面的布局才更加精彩。
多图片的布局
上面一直在说的都单张图片的显示。只要我们把图片用合适的方式放进容器即可。如果是图片列表呢?或者专门展示图片的网站会有大量的图片而且尺寸和比例千奇百怪,各不相同。
假设要想实现一个图片画廊效果,首先我们给图片一个 float: left
,但是由于图片的尺寸不一样,导致每个图片的高度不同,下一行的图片就会卡住,导致布局错乱。
此时你不得不给容器设置高度,让图片能够底部对齐,但在文章一开始我们也提到了,这时候图片要么超出容器的高度,要么留有空白。
那如果使用 object-fit
属性按照业务需求去控制内容,貌似可以完成任务。
我们把值设为 contain,布局是没有问题了,但是其实很不美观。如果设为 cover,如果图片过大很多内容都会丢失看不到。 怎么办?有什么解决办法?这时候就是瀑布流布局的优势了。
瀑布流布局即不会出现错乱现象,而且会最大限度显示图片的内容。所以是众多图片网站选择的布局方式。
而瀑布流布局目前有两种形式:一是等宽型,二是等高型。我们先来说说等宽型。
等宽瀑布流
看到上面你实现的思路是什么?可以思考几秒,接下来一起来看这些实现方式中有没有和你一思路一样的。
思路1. JS 计算列数
关键思路:
- 首先设置列宽度,然后计算能够展示的列数。
- 向每一列中添加图片。
关键代码:
<script>
export default {
methods: {
//计算图片列数
getColNumbers() {
let clientWidth = this.$refs.waterfall.clientWidth
this.colNumbers = Math.floor(clientWidth / this.colWidth)
},
//读取图片
loadImage() {
this.getColNumbers()
for (let i = 0; i < 17; i++) {
let colIndex = i % this.colNumbers
let url = require(`@/assets/images/${i}.jpg`)
if (this.imgList[colIndex]) {
this.imgList[colIndex].push(url)
} else {
this.$set(this.imgList, colIndex, [url])
}
}
},
}
}
</script>
优势:思路清晰简单,不需要做过多的计算,只要计算需要显示的列数然后添加图片即可。
劣势:每列的末尾可能不够友好,可能出现有些列会很长,有些又会很短。
思路2. 利用绝对定位
关键思路:
- 首先设置列宽度,然后计算能够展示的列数。
- 把图片设置为绝对定位,然后计算出每个图片的top,left值。
- 先把第一行图片排好,top 为 0,left 为 列的索引*列宽。
- 从第二行开始,每张图片都放到最短的一列下面。然后增加此列高度,此时列的高度发生变化,下张图片又会寻找其他最短的列。以此持续计算下去。
关键代码:
<script>
export default {
methods: {
//计算图片列数
getColNumbers() {
let clientWidth = this.$refs.waterfall.clientWidth
this.colNumbers = Math.floor(clientWidth / this.colWidth)
},
//读取图片
loadImage() {
this.getColNumbers()
for (let i = 0; i < 17; i++) {
let image = new Image()
let url = require(`@/assets/images/${i}.jpg`)
image.src = url
image.onload = () => {
this.render({
index: i,
url: url,
ratio: image.width / image.height
})
}
}
},
render(imgInfo) {
let colIndex = imgInfo.index % this.colNumbers
imgInfo.left = colIndex * this.colWidth
//首行 top为 0,记录每列的高度
if (imgInfo.index < this.colNumbers) {
imgInfo.top = 0
this.colHeight[colIndex] = this.colWidth / imgInfo.ratio
} else {
//获取高度的最小值
let minHeight = Math.min.apply(null, this.colHeight)
let minIndex = this.colHeight.indexOf(minHeight)
//此图片的 top 为上面图片的高度,left 相等
imgInfo.top = minHeight
imgInfo.left = minIndex * this.colWidth
//把高度加上去
this.colHeight[minIndex] += this.colWidth / imgInfo.ratio
}
this.imgList.push(imgInfo)
}
}
}
</script>
优势:因为每次追加的图片都是最短列,所以末尾的展示会比思路 1 中要友好很多。
劣势:没渲染一张都会计算一次 top,left 值。而且图片的顺序是打乱的。
思路3. CSS3 column 属性
关键思路:
- column-count:指定列数
- column-gap: 设置列之间的间距
关键代码:
<template>
<div class="waterfall-width-column">
<div class="image-box" v-for="img in imgList" :key="img">
<img :src="img" alt="" />
</div>
</div>
</template>
<style lang="scss" scoped>
.waterfall-width-column {
column-count: 3;
column-gap: 10px;
.image-box {
img {
display: block;
width: 100%;
}
}
}
</style>
优势:更加简单,不用额外计算,直接使用CSS渲染高效。
劣势:图片的顺序是从上向下排列的,这个要看业务需求允不允许了。另外列数固定。
不过你可以尝试通过媒体查询设置不同列数
@media (min-width: 768px) {
.waterfall-width-column {
column-count: 3;
}
}
@media (min-width: 992px) {
.waterfall-width-column {
column-count: 4;
}
}
@media (min-width: 1200px) {
.waterfall-width-column {
column-count: 6;
}
}
等高瀑布流
说完了等宽型接下来我们来说说等高型。
思路1. JS计算缩放
- 首先给定一个基准高度
- 图片获取基准高度下的宽度,然后计算每一行能够放入多少张
- 此时每一行图片肯定会小于容器宽度,然后这一行进行缩放到容器大小。在重新计算放大后的高度。
关键代码:
<script>
export default {
data() {
return {
baseHeight: 200, //图片的基础计算高度
imgList: [[]], //用二维数据保存每一行数据
rowWidth: 0, //每行的图片宽度
rowCount: 0 //每行的索引
}
},
methods: {
loadImage() {
for (let i = 0; i < 17; i++) {
let image = new Image()
let url = require(`@/assets/images/${i}.jpg`)
image.src = url
image.onload = () => {
this.compare({
url: url,
width: this.baseHeight * (image.width / image.height),
height: this.baseHeight
})
}
}
},
//缩放后的总图片宽度与屏幕宽度比较
compare(image) {
//容器宽度
let clientWidth = this.$refs.waterfall.clientWidth
//计算每行宽度
this.rowWidth += image.width
//如果宽度大于容器宽度,去掉多余的宽度,整体进行缩放适应容器让右边对齐
if (this.rowWidth > clientWidth) {
//减去每个css padding边距
clientWidth = clientWidth - this.imgList[this.rowCount].length * 10
this.rowWidth = this.rowWidth - image.width
//把高度调整为放大后的
let growAfterHeight = (clientWidth * this.baseHeight) / this.rowWidth
this.imgList[this.rowCount].forEach(item => {
item.height = growAfterHeight
})
//把多余图片放入到下一行
this.rowWidth = image.width
this.rowCount++
this.$set(this.imgList, this.rowCount, [image])
} else {
this.imgList[this.rowCount].push(image)
}
}
}
}
</script>
优势:图片的内容得到全部展示,不会被隐藏。
劣势:需要反复计算以及缩放。
思路2. Flex布局
- 首先给图片一个固定高度,然后利用flex-grow的比例分配的特性
- 给图片设定object-fit属性让其保持比例充满容器
<template>
<div class="waterfall-height-css">
<div class="image-box" v-for="img in imgList" :key="img.url">
<img :src="img.url" />
</div>
</div>
</template>
<script>
<style lang="scss" scoped>
.waterfall-height-css {
display: flex;
flex-wrap: wrap;
.image-box {
flex-grow: 1;
}
img {
display: block;
min-width: 100%;
height: 200px;
object-fit: cover;
}
}
</style>
此时你会发现,每一行的图片都得到了很好的显示效果。但是唯独最后一行会出现一个小小的问题。
想象一下,假如最后一行只有一张图片的话,他会被缩放到充满一行,导致图片只会显示非常小的一部分内容。
所以,我们最后一行的图片不进行缩放处理即可。只需要添加以下css属性即可。
<style lang="scss" scoped>
.waterfall-height-css {
&:after {
content: '';
display: block;
flex-grow: 99999;
}
}
</style>
因为flex-grow: 99999
的值非常大,所以会把最后一行的剩余空间几乎全部占用,导致图片分配不了,只会按照原尺寸显示,就不会缩放占满一行啦。
优势:css 设置简单,渲染高效。
劣势:会损失图片的一部分可见区域。
到此,我们介绍了图片的显示特性以及如何利用 object-fit
进行内容的控制。
对于多图片的布局,要想比较合理的显示图片,瀑布流布局是非常好的选择,当然如果业务需求对图片的展示友好度及美观度不做要求,你大可利用 object-fit
控制内容即可。
但是我认为瀑布流布局也是我们应该掌握的内容之一,即便此时用不到,也可以先把文章收藏起来,以备不时之需,文中采用了多种方式的实现,你可以选择一种最贴合你需求的方式。
当然,案例中其实还有很多细节没有处理,比如浏览器窗口发上变化时重新加载图片会发生闪动该如何优化体验?小伙伴们不妨自己去尝试进行优化。动手实践是掌握技能的重要手段。
文中所有案例的代码地址:gitHub地址
yii2 swift mailer 发送邮件不成功的问题
今天调试yii2自带的swift mailer发邮件,开始调试时,发送用的send()方法始终返回true,但是就是没有收到邮件,很是纳闷,于是开始了半个晚上的调试之旅,我把调试过程发出来,希望后面的小伙伴能少走一些弯路。
首先,根据热心网友的文章,配置邮箱的基础信息,我用的base项目,所以我的配置文件是web.php,配置内容为:
‘mailer’ => [
‘class’ => ‘yii\swiftmailer\Mailer’,
‘transport’ => [
‘class’ => ‘Swift_SmtpTransport’,
‘host’ => ‘smtp.163.com’,
‘username’ => ‘xxx@163.com’,
‘password’ => ‘xxx’,
‘port’ => ’25’,
‘encryption’ => ‘tls’,// tls | ssl
],
‘messageConfig’=>[
‘charset’=>’UTF-8’,
‘from’=>[‘xxx@163.com’=>’admin’]
],
‘useFileTransport’ => false,
],
然后开始写了一个测试用的控制器,内容如下:
public function actionMailer()
{
$mail= Yii::$app->mailer->compose();
$mail->setTo(‘10000@qq.com’);
$mail->setSubject(“Test title”);
$mail->setTextBody(‘Test content’);
//$mail->setHtmlBody(“Test HTML”);
var_dump($mail->send());
}
好了,信心满满的开始测试了,执行链接,满心欢喜的去查看QQ邮箱了,但是左等右等,前等后等,丫的就是没有,重新发送,还是没有,但是send()的返回值明明就是true!
于是开始排错,总结了一下,出错的可能位置在以下几点:
不同邮箱的host是不同的
如果是163邮箱,password是授权码
userFileTransport要设置成false,否则只会在runtime下生成缓存文件,不会真正发送
一一排查,确信没有错误,太奇怪了,于是我决定直接调用swiftmailer试一下,于是新建了下面的控制器:
public function actionMailer2()
{
$mailer = new \yii\swiftmailer\Mailer();
$mailer->transport=[
‘class’ => ‘Swift_SmtpTransport’,
‘host’ => ‘smtp.163.com’,
‘username’ => ‘xxxx@163.com’,
‘password’ => ‘xxxx’,
‘port’ => ’25’,
‘encryption’ => ‘tls’,// tls | ssl
];
$mailer->messageConfig=[
‘charset’=>’UTF-8’,
‘from’=>[‘xxxx@163.com’=>’admin’]
];
$mailer->useFileTransport = false;
$mail= $mailer->compose();
$mail->setTo(‘10000@qq.com’);
$mail->setSubject(“Test title”);
$mail->setTextBody(‘Test content’);
var_dump($mail->send());
}
哎哟我去!发送成功了!这是为啥呢?首先证明这个类确是是可以发送邮件的了,那么下一步要细细的分析这两段代码区别在何处了。
我分别打印了两个类构造完成后的结果:
public function actionMailer()
{
echo ‘<pre>’;
var_dump(Yii::$app->mailer);
}
和
public function actionMailer2()
{
$mailer = new \yii\swiftmailer\Mailer();
$mailer->transport=[
‘class’ => ‘Swift_SmtpTransport’,
‘host’ => ‘smtp.163.com’,
‘username’ => ‘xxxx@163.com’,
‘password’ => ‘xxxx’,
‘port’ => ’25’,
‘encryption’ => ‘tls’,// tls | ssl
];
$mailer->messageConfig=[
‘charset’=>’UTF-8’,
‘from’=>[‘xxxx@163.com’=>’admin’]
];
$mailer->useFileTransport = false;
echo ‘<pre>’;
var_dump($mailer);
}
在长长的内容中慢慢的对比,终于发现了端倪,
第一个方法中:[“useFileTransport”]=>bool(true)
第二个方法中:[“useFileTransport”]=>bool(false)
上面也说过了,useFileTransport必须设置成false才能发送成功,那问题总算是找到了,那为什么会出现这样的问题呢?我上面明明是设置过的,为什么没生效?难道是测试环境的原因吗?
我测试使用的url是http://goonwin.com/index-test.php?r=liyang/mailer,注意那个index-test.php,就是他的原因了,于是我把index-test.php改成index.php立马发送成功了,丫的,这么隐蔽一个坑啊!!!
但是我还是希望测试环境可以发送邮件的,于是,稍微改动了一下调用的方法:
public function actionMailer()
{
$mail = Yii::$app->mailer;
$mail->useFileTransport = false;
$mail= $mail->compose();
$mail->setTo(‘10000@qq.com’);
$mail->setSubject(“Test title”);
$mail->setTextBody(‘Test content’);
var_dump($mail->send());
}
Oh,终于可以发送了,看到论坛中有些朋友也在问同样的问题,估计现在你已经可以解决了。洋洋洒洒写了这么多,其实就一个目的,希望能给新手一点调试的经验吧,老手你当然乐呵乐呵就得了。
浪费一晚上青春,睡觉!
JS压缩方法(使用uglify工具在本地压缩)及批量压缩
一、压缩JS的好处:
1、减小了文件的体积
2、减小了网络传输量和带宽占用
3、减小了服务器的处理的压力
4、提高了页面的渲染显示的速度
二、压缩JS的方法如下:
1、首先在本地安装node.js和npm,一般npm集成于nodejs,即安装nodejs,同时也安装了npm。
2、安装uglify插件。在cmd命令行执行:
3、开始压缩js文件。
首先在命令行,进入JS文件所在目录。
然后执行压缩文件命令:uglifyjs pdf.js -o pdf.min.js
我要压缩的文件为pdf.js,生成的压缩文件名为pdf.min.js。-o是一个参数,关于参数的具体解析大家可以去官网看,https://www.npmjs.com/package/uglify-js。最后看一看压缩的结果:
可以看到文件由719KB,被压缩到了536KB。
三、批量压缩方法:
1)新建txt文件,内容如下
@echo off :: 设置压缩JS文件的根目录,脚本会自动按树层次查找和压缩所有的JS(注意路劲中不能有空格) SET JSFOLDER=D:\uglifyDestination echo 正在查找JS文件 chdir /d %JSFOLDER% for /r . %%a in (*.js) do ( @echo 正在压缩 %%~a ... uglifyjs %%~fa -m -o %%~fa ) echo 完成! pause & exit
2)另存为.bat文件
3)将需要压缩的js文件放置目录
4)运行bat文件
详解JavaScript+Canvas绘制环形进度条
目录
效果图
思考
移动端的场景里经常会出现环形进度条的功能,在实现这个功能前,我预想的解决方案大致有: echarts、antv、canvas、svg
前面两种第三方提供的解决方案当然是简单,拿到案例修整一下即可,但是需要下载依赖,而且代码量不小。有没有不需要依赖第三方包,采用原生的写法,独立封装成一个组件,降低耦合,而且性能优越?
当然,那就主要介绍canvas的使用
实现思路
可以展示整个圆、半圆以及任意角度弧形(左右对称)的进度条。整体思路如下:
1.先确定展示的形状,是整个圆、半圆还是一般的弧形
2.把确定好形状的圆弧均分100等份,计算出每一份所占的弧度
3.灰色圆弧占100份,红色圆弧最终占的份数由参数确定
4.设置setInterval定时器,重复执行画图操作
- 清空画布
- 先画灰色的圆弧,占100份
- 再画红色的圆弧:红色圆弧的份数从0开始,每次加1
- 画红色圆弧末端的红色圆:难点在于根据角度确定红色圆的圆心,这里面涉及到三角函数,在草稿纸上画个图就大致明白了
- 当红色圆弧的份数达到指定值(传的参数)的时候,清除定时器
具体代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Canvas</title>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
<script>
draw(66);
/**
* [顺时针方向画图,起始点在左侧]
* @param {[number]} percent [所占的进度百分比,比如66%,则传66即可,0 <= percent <= 100]
* @param {[number]} sR [圆弧起始角度,可不传,默认是π/2,Math.PI/2 <= sR < 3/2 * Math.PI]
*/
function draw(percent, sR) {
if (percent < 0 || percent > 100) {
return;
}
if (sR < Math.PI/2 || sR >= 3/2 * Math.PI) {
return;
}
var canvas = document.querySelector('#canvas'),
cxt = canvas.getContext('2d'),
cWidth = canvas.width,
cHeight = canvas.height,
baseColor = '#e1e1e1',
coverColor = '#fe4d43',
PI = Math.PI,
sR = sR || 1/2 * PI; // 默认圆弧的起始点弧度为π/2
var finalRadian = sR + ((PI + (PI - sR) * 2) * percent / 100); // 红圈的终点弧度
var step = (PI + (PI - sR) * 2)/100; // 一个1%对应的弧度大小
var text = 0; // 显示的数字
var timer = setInterval(function() {
cxt.clearRect(0, 0, cWidth, cHeight);
var endRadian = sR + text * step;
// 画灰色圆弧
drawCanvas(cWidth/2, cHeight/2, 80, sR, sR + (PI + (PI - sR) * 2), baseColor, 2);
// 画红色圆弧
drawCanvas(cWidth/2, cHeight/2, 80, sR, endRadian, coverColor, 2);
// 画红色圆头
// 红色圆头其实就是一个圆,关键的是找到其圆心,涉及到三角函数知识,自己画个图一看就明了
var angle = 2*PI - endRadian; // 转换成逆时针方向的弧度(三角函数中的)
xPos = Math.cos(angle) * 80 + cWidth/2; // 红色圆 圆心的x坐标
yPos = -Math.sin(angle) * 80 + cHeight/2; // 红色圆 圆心的y坐标
drawCanvas(xPos, yPos, 2, 0, 2*PI, coverColor, 2);
// 数字
cxt.fillStyle = coverColor;
cxt.font = '40px PT Sans';
var textWidth = cxt.measureText(text+'%').width;
cxt.fillText(text+'%', cWidth/2 - textWidth/2, cHeight/2 + 15);
text++;
if (endRadian.toFixed(2) >= finalRadian.toFixed(2)) {
clearInterval(timer);
}
}, 30);
function drawCanvas(x,y,r,sRadian,eRadian,color,lineWidth) {
cxt.beginPath();
cxt.lineCap = "round";
cxt.strokeStyle = color;
cxt.lineWidth = lineWidth;
cxt.arc(x, y, r, sRadian, eRadian, false);
cxt.stroke();
}
}
</script>
</body>
</html>
关于动画部分,可以使用requestAnimationFrame
做优化,函数改写如下:
function draw(percent, sR) {
if (percent < 0 || percent > 100) {
return;
}
if (sR < Math.PI/2 || sR >= 3/2 * Math.PI) {
return;
}
var canvas = document.querySelector('#canvas'),
cxt = canvas.getContext('2d'),
cWidth = canvas.width,
cHeight = canvas.height,
baseColor = '#e1e1e1',
coverColor = '#fe4d43',
PI = Math.PI,
sR = sR || 1/2 * PI; // 默认圆弧的起始点弧度为π/2
var finalRadian = sR + ((PI + (PI - sR) * 2) * percent / 100); // 红圈的终点弧度
var step = (PI + (PI - sR) * 2)/100; // 一个1%对应的弧度大小
var text = 0; // 显示的数字
window.requestAnimationFrame(paint);
function paint() {
cxt.clearRect(0, 0, cWidth, cHeight);
var endRadian = sR + text * step;
// 画灰色圆弧
drawCanvas(cWidth/2, cHeight/2, 80, sR, sR + (PI + (PI - sR) * 2), baseColor, 2);
// 画红色圆弧
drawCanvas(cWidth/2, cHeight/2, 80, sR, endRadian, coverColor, 2);
// 画红色圆头
// 红色圆头其实就是一个圆,关键的是找到其圆心,涉及到三角函数知识,自己画个图一看就明了
var angle = 2*PI - endRadian; // 转换成逆时针方向的弧度(三角函数中的)
xPos = Math.cos(angle) * 80 + cWidth/2; // 红色圆 圆心的x坐标
yPos = -Math.sin(angle) * 80 + cHeight/2; // 红色圆 圆心的y坐标
drawCanvas(xPos, yPos, 2, 0, 2*PI, coverColor, 2);
// 数字
cxt.fillStyle = coverColor;
cxt.font = '40px PT Sans';
var textWidth = cxt.measureText(text+'%').width;
cxt.fillText(text+'%', cWidth/2 - textWidth/2, cHeight/2 + 15);
text++;
if (endRadian.toFixed(2) < finalRadian.toFixed(2)) {
window.requestAnimationFrame(paint);
}
}
function drawCanvas(x,y,r,sRadian,eRadian,color,lineWidth) {
cxt.beginPath();
cxt.lineCap = "round";
cxt.strokeStyle = color;
cxt.lineWidth = lineWidth;
cxt.arc(x, y, r, sRadian, eRadian, false);
cxt.stroke();
}
window 安装composer (package not found)
打开 php 的 openssl 扩展。
浏览器访问:getcomposer.org/installer, 进入安装程序。
安装完后,composer -v 查看版本。
换镜像: composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/